diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs index 8bfa775b936f4..b4567dc1a70c8 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Symbols.cs @@ -474,10 +474,18 @@ internal NamespaceOrTypeOrAliasSymbolWithAnnotations BindNamespaceOrTypeOrAliasS var refToken = refTypeSyntax.RefKeyword; if (!syntax.HasErrors) { - diagnostics.Add(ErrorCode.ERR_UnexpectedToken, refToken.GetLocation(), refToken.ToString()); + // PROTOTYPE(delegate-type-args): restore this diagnostic in non-delegate call sites + // diagnostics.Add(ErrorCode.ERR_UnexpectedToken, refToken.GetLocation(), refToken.ToString()); } - return BindNamespaceOrTypeOrAliasSymbol(refTypeSyntax.Type, diagnostics, basesBeingResolved, suppressUseSiteDiagnostics); + var referencedType = BindNamespaceOrTypeOrAliasSymbol(refTypeSyntax.Type, diagnostics, basesBeingResolved, suppressUseSiteDiagnostics); + if (referencedType.IsType) + { + return TypeWithAnnotations.Create(new RefTypeSymbol(RefKind.Ref, referencedType.TypeWithAnnotations)); + } + + // PROTOTYPE(delegate-type-args): handle aliases + return createErrorType(); } default: @@ -1171,6 +1179,20 @@ private TypeWithAnnotations BindGenericSimpleNamespaceOrTypeOrAliasSymbol( { resultType = unconstructedType.Construct(PlaceholderTypeArgumentSymbol.CreateTypeArguments(unconstructedType.TypeParameters)); } + else if (unconstructedType.IsDelegateType()) + { + var types = BindDelegateTypeArguments(typeArguments, diagnostics, basesBeingResolved); + // PROTOTYPE(delegate-type-args): assert that if we are looking up an attribute, there must be other errors + // PROTOTYPE(delegate-type-args): check validity of ref kinds etc in delegate signature + resultType = unconstructedType.Construct(types); + if (ShouldCheckConstraints && ConstraintsHelper.RequiresChecking(resultType)) + { + bool includeNullability = Compilation.IsFeatureEnabled(MessageID.IDS_FeatureNullableReferenceTypes); + resultType.CheckConstraintsForNamedType(new ConstraintsHelper.CheckConstraintsArgs(this.Compilation, this.Conversions, includeNullability, node.Location, diagnostics), + node, typeArguments, basesBeingResolved); + } + + } else { var boundTypeArguments = BindTypeArguments(typeArguments, diagnostics, basesBeingResolved); @@ -1284,6 +1306,30 @@ private ExtendedErrorTypeSymbol CreateErrorIfLookupOnTypeParameter( return null; } + private ImmutableArray BindDelegateTypeArguments(SeparatedSyntaxList typeArguments, BindingDiagnosticBag diagnostics, ConsList basesBeingResolved = null) + { + Debug.Assert(typeArguments.Count > 0); + var types = ArrayBuilder.GetInstance(); + foreach (var argSyntax in typeArguments) + { + var type = BindDelegateTypeArgument(argSyntax, diagnostics, basesBeingResolved); + types.Add(type); + } + + return types.ToImmutableAndFree(); + } + + private TypeWithAnnotations BindDelegateTypeArgument(TypeSyntax typeArgument, BindingDiagnosticBag diagnostics, ConsList basesBeingResolved = null) + { + var binder = this.WithAdditionalFlags(BinderFlags.SuppressUnsafeDiagnostics); + + var symbol = BindTypeOrAlias(typeArgument, diagnostics, basesBeingResolved); + var alias = UnwrapAlias(symbol, diagnostics, typeArgument, basesBeingResolved).TypeWithAnnotations; + + // PROTOTYPE: we should allow pointers, ref etc in this code path while continuing to disallow in the regular 'BindTypeArgument' + return alias; + } + private ImmutableArray BindTypeArguments(SeparatedSyntaxList typeArguments, BindingDiagnosticBag diagnostics, ConsList basesBeingResolved = null) { Debug.Assert(typeArguments.Count > 0); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs index 4a7a1bd74aa92..02357277f2204 100644 --- a/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs +++ b/src/Compilers/CSharp/Portable/Emitter/Model/PEModuleBuilder.cs @@ -1134,6 +1134,9 @@ internal sealed override Cci.ITypeReference Translate( case SymbolKind.FunctionPointerType: return Translate((FunctionPointerTypeSymbol)typeSymbol); + + case SymbolKind.RefType: + return Translate((RefTypeSymbol)typeSymbol); } throw ExceptionUtilities.UnexpectedValue(typeSymbol.Kind); @@ -1502,6 +1505,11 @@ internal Cci.IPointerTypeReference Translate(PointerTypeSymbol symbol) return (Cci.IPointerTypeReference)GetCciAdapter(symbol); } + internal Cci.IRefTypeReference Translate(RefTypeSymbol symbol) + { + return (Cci.IRefTypeReference)GetCciAdapter(symbol); + } + internal Cci.IFunctionPointerTypeReference Translate(FunctionPointerTypeSymbol symbol) { return (Cci.IFunctionPointerTypeReference)GetCciAdapter(symbol); diff --git a/src/Compilers/CSharp/Portable/Emitter/Model/RefTypeSymbolAdapter.cs b/src/Compilers/CSharp/Portable/Emitter/Model/RefTypeSymbolAdapter.cs new file mode 100644 index 0000000000000..185f6eb54ca27 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Emitter/Model/RefTypeSymbolAdapter.cs @@ -0,0 +1,157 @@ +// 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. + +#nullable disable + +using System.Collections.Immutable; +using System.Reflection.Metadata; +using Microsoft.CodeAnalysis.CSharp.Emit; +using Microsoft.CodeAnalysis.Emit; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + internal partial class +#if DEBUG + RefTypeSymbolAdapter : SymbolAdapter, +#else + RefTypeSymbol : +#endif + Cci.IRefTypeReference + { + Cci.ITypeReference Cci.IRefTypeReference.GetReferencedType(EmitContext context) + { + var type = ((PEModuleBuilder)context.Module).Translate(AdaptedRefTypeSymbol.ReferencedTypeWithAnnotations.Type, syntaxNodeOpt: (CSharpSyntaxNode)context.SyntaxNode, diagnostics: context.Diagnostics); + + if (AdaptedRefTypeSymbol.ReferencedTypeWithAnnotations.CustomModifiers.Length == 0) + { + return type; + } + else + { + return new Cci.ModifiedTypeReference(type, ImmutableArray.CastUp(AdaptedRefTypeSymbol.ReferencedTypeWithAnnotations.CustomModifiers)); + } + } + + bool Cci.ITypeReference.IsEnum + { + get { return false; } + } + + bool Cci.ITypeReference.IsValueType + { + get { return false; } + } + + Cci.ITypeDefinition Cci.ITypeReference.GetResolvedType(EmitContext context) + { + return null; + } + + Cci.PrimitiveTypeCode Cci.ITypeReference.TypeCode + { + get { return Cci.PrimitiveTypeCode.Reference; } + } + + TypeDefinitionHandle Cci.ITypeReference.TypeDef + { + get { return default(TypeDefinitionHandle); } + } + + Cci.IGenericMethodParameterReference Cci.ITypeReference.AsGenericMethodParameterReference + { + get { return null; } + } + + Cci.IGenericTypeInstanceReference Cci.ITypeReference.AsGenericTypeInstanceReference + { + get { return null; } + } + + Cci.IGenericTypeParameterReference Cci.ITypeReference.AsGenericTypeParameterReference + { + get { return null; } + } + + Cci.INamespaceTypeDefinition Cci.ITypeReference.AsNamespaceTypeDefinition(EmitContext context) + { + return null; + } + + Cci.INamespaceTypeReference Cci.ITypeReference.AsNamespaceTypeReference + { + get { return null; } + } + + Cci.INestedTypeDefinition Cci.ITypeReference.AsNestedTypeDefinition(EmitContext context) + { + return null; + } + + Cci.INestedTypeReference Cci.ITypeReference.AsNestedTypeReference + { + get { return null; } + } + + Cci.ISpecializedNestedTypeReference Cci.ITypeReference.AsSpecializedNestedTypeReference + { + get { return null; } + } + + Cci.ITypeDefinition Cci.ITypeReference.AsTypeDefinition(EmitContext context) + { + return null; + } + + void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor) + { + visitor.Visit((Cci.IPointerTypeReference)this); + } + + Cci.IDefinition Cci.IReference.AsDefinition(EmitContext context) + { + return null; + } + } + + internal partial class RefTypeSymbol + { + +#if DEBUG + private RefTypeSymbolAdapter _lazyAdapter; + + protected sealed override SymbolAdapter GetCciAdapterImpl() => GetCciAdapter(); + + internal new RefTypeSymbolAdapter GetCciAdapter() + { + if (_lazyAdapter is null) + { + return InterlockedOperations.Initialize(ref _lazyAdapter, new RefTypeSymbolAdapter(this)); + } + + return _lazyAdapter; + } +#else + internal RefTypeSymbol AdaptedRefTypeSymbol => this; + + internal new RefTypeSymbol GetCciAdapter() + { + return this; + } +#endif + } + +#if DEBUG + internal partial class RefTypeSymbolAdapter + { + internal RefTypeSymbolAdapter(RefTypeSymbol underlyingRefTypeSymbol) + { + AdaptedRefTypeSymbol = underlyingRefTypeSymbol; + } + + internal sealed override Symbol AdaptedSymbol => AdaptedRefTypeSymbol; + internal RefTypeSymbol AdaptedRefTypeSymbol { get; } + } +#endif +} diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs index 50e437c51a549..c896dbeb6c9dd 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Types.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp { + // PROTOTYPE(delegate-type-args): symbol display internal partial class SymbolDisplayVisitor { public override void VisitArrayType(IArrayTypeSymbol symbol) diff --git a/src/Compilers/CSharp/Portable/Symbols/PublicModel/RefTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/PublicModel/RefTypeSymbol.cs new file mode 100644 index 0000000000000..bd3ff41cfba7e --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/PublicModel/RefTypeSymbol.cs @@ -0,0 +1,34 @@ +// 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.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols.PublicModel +{ + // PROTOTYPE(delegate-type-arg): public model? + // internal sealed class RefTypeSymbol : TypeSymbol + // { + // private readonly Symbols.RefTypeSymbol _underlying; + + // internal override Symbols.TypeSymbol UnderlyingTypeSymbol => _underlying; + // internal override Symbols.NamespaceOrTypeSymbol UnderlyingNamespaceOrTypeSymbol => _underlying; + // internal override CSharp.Symbol UnderlyingSymbol => _underlying; + + // protected override void Accept(SymbolVisitor visitor) + // { + // _underlying.ReferencedTypeWithAnnotations.Accept + // } + + // protected override TResult Accept(SymbolVisitor visitor) + // { + // throw new System.NotImplementedException(); + // } + + // protected override ITypeSymbol WithNullableAnnotation(CodeAnalysis.NullableAnnotation nullableAnnotation) + // { + // throw new System.NotImplementedException(); + // } + // } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/RefTypeSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/RefTypeSymbol.cs new file mode 100644 index 0000000000000..e3aa3235bee72 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Symbols/RefTypeSymbol.cs @@ -0,0 +1,146 @@ +// 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. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols +{ + /// Represents a 'ref'/'in'/'out' type. Should only be used in delegate type arguments. + internal partial class RefTypeSymbol : TypeSymbol + { + public RefKind RefKind { get; } + public TypeWithAnnotations ReferencedTypeWithAnnotations { get; } + + public RefTypeSymbol(RefKind refKind, TypeWithAnnotations referencedTypeWithAnnotations) + { + RefKind = refKind; + ReferencedTypeWithAnnotations = referencedTypeWithAnnotations; + } + + public override bool IsReferenceType => false; + public override bool IsValueType => false; + + public override TypeKind TypeKind => TypeKind.Ref; + + public override bool IsRefLikeType => false; + + public override bool IsReadOnly => false; + + public override SymbolKind Kind => SymbolKind.RefType; + + public override Symbol? ContainingSymbol => null; + + public override ImmutableArray Locations => ImmutableArray.Empty; + + public override ImmutableArray DeclaringSyntaxReferences => ImmutableArray.Empty; + + public override Accessibility DeclaredAccessibility => throw ExceptionUtilities.Unreachable; + + public override bool IsStatic => false; + + public override bool IsAbstract => false; + + public override bool IsSealed => false; + + internal override NamedTypeSymbol? BaseTypeNoUseSiteDiagnostics => null; + + internal override bool IsRecord => false; + + internal override bool IsRecordStruct => false; + + internal override ObsoleteAttributeData? ObsoleteAttributeData => null; + + public override void Accept(CSharpSymbolVisitor visitor) + { + ReferencedTypeWithAnnotations.Type.Accept(visitor); + } + + public override TResult Accept(CSharpSymbolVisitor visitor) + { + return ReferencedTypeWithAnnotations.Type.Accept(visitor); + } + + public override ImmutableArray GetMembers() + { + throw ExceptionUtilities.Unreachable; + } + + public override ImmutableArray GetMembers(string name) + { + throw ExceptionUtilities.Unreachable; + } + + public override ImmutableArray GetTypeMembers() + { + throw ExceptionUtilities.Unreachable; + } + + public override ImmutableArray GetTypeMembers(string name) + { + throw ExceptionUtilities.Unreachable; + } + + protected override ISymbol CreateISymbol() + { + // PROTOTYPE(delegate-type-args): create a real public symbol? + return ReferencedTypeWithAnnotations.Type.ISymbol; + } + + protected override ITypeSymbol CreateITypeSymbol(CodeAnalysis.NullableAnnotation nullableAnnotation) + { + return ReferencedTypeWithAnnotations.Type.GetITypeSymbol(default); // PROTOTYPE(delegate-type-args) + } + + internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument a) + { + return ReferencedTypeWithAnnotations.Type.Accept(visitor, a); + } + + internal override void AddNullableTransforms(ArrayBuilder transforms) + { + ReferencedTypeWithAnnotations.Type.AddNullableTransforms(transforms); + } + + internal override bool ApplyNullableTransforms(byte defaultTransformFlag, ImmutableArray transforms, ref int position, out TypeSymbol result) + { + return ReferencedTypeWithAnnotations.Type.ApplyNullableTransforms(defaultTransformFlag, transforms, ref position, out result); + } + + internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo useSiteInfo) + { + throw ExceptionUtilities.Unreachable; + } + + internal override bool GetUnificationUseSiteDiagnosticRecursive(ref DiagnosticInfo result, Symbol owner, ref HashSet checkedTypes) + { + throw ExceptionUtilities.Unreachable; + } + + internal override ImmutableArray InterfacesNoUseSiteDiagnostics(ConsList? basesBeingResolved = null) + { + throw ExceptionUtilities.Unreachable; + } + + internal override TypeSymbol MergeEquivalentTypes(TypeSymbol other, VarianceKind variance) + { + throw ExceptionUtilities.Unreachable; + } + + internal override TypeSymbol SetNullabilityForReferenceTypes(Func transform) + { + throw ExceptionUtilities.Unreachable; + } + + internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls() + { + throw ExceptionUtilities.Unreachable; + } + } +} diff --git a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs index ee1c79f6baba9..0260410d2e06f 100644 --- a/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/SubstitutedParameterSymbol.cs @@ -44,7 +44,8 @@ public override Symbol ContainingSymbol get { return _containingSymbol; } } - public override TypeWithAnnotations TypeWithAnnotations + // PROTOTYPE(delegate-type-args): should any of these members move up to WrappedParameterSymbol? + private TypeWithAnnotations PossibleRefTypeWithAnnotations { get { @@ -67,6 +68,37 @@ public override TypeWithAnnotations TypeWithAnnotations } } + public override TypeWithAnnotations TypeWithAnnotations + { + get + { + var type = PossibleRefTypeWithAnnotations; + if (type.Type is RefTypeSymbol refType) + { + return refType.ReferencedTypeWithAnnotations; + } + else + { + return type; + } + } + } + + public override RefKind RefKind + { + get + { + if (PossibleRefTypeWithAnnotations.Type is RefTypeSymbol refType) + { + return refType.RefKind; + } + else + { + return base.RefKind; + } + } + } + internal override ImmutableArray InterpolatedStringHandlerArgumentIndexes => _underlyingParameter.InterpolatedStringHandlerArgumentIndexes; internal override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 66c1e046addb4..31b63cd9ec6c2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -776,6 +776,10 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo break; } + case TypeKind.Ref: + next = ((RefTypeSymbol)current).ReferencedTypeWithAnnotations; + break; + default: throw ExceptionUtilities.UnexpectedValue(current.TypeKind); } diff --git a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs index 512e978ab86c3..b078d2acc6b23 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Wrapped/WrappedParameterSymbol.cs @@ -43,7 +43,7 @@ public override TypeWithAnnotations TypeWithAnnotations get { return _underlyingParameter.TypeWithAnnotations; } } - public sealed override RefKind RefKind + public override RefKind RefKind { get { return _underlyingParameter.RefKind; } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs index 3272fbc03002f..584e7b6432b3d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/Source/DelegateTests.cs @@ -790,5 +790,94 @@ public static void Main() Assert.True(lambda.ReturnsByRefReadonly); Assert.Equal(RefKind.In, lambda.Parameters[0].RefKind); } + + [Fact] + public void DelegateRefTypeArgument_01() + { + var source = @" +using System; + +C.M1(C.M0); + +class C +{ + public static void M0(int x) + { + x++; + } + + public static void M1(Action action) + { + int i = 0; + Console.Write(i); + action(ref i); + Console.Write(i); + } +}"; + // PROTOTYPE: requires updated runtime support to verify output + var verifier = CompileAndVerify(source/*, expectedOutput: "01"*/); + verifier.VerifyDiagnostics(); + // PROTOTYPE: requires updated symbol display for meaningful-ish disassembly + verifier.VerifyIL("C.M1", @" +{ + // Code size 23 (0x17) + .maxstack 2 + .locals init (int V_0) //i + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: call ""void System.Console.Write(int)"" + IL_0008: ldarg.0 + IL_0009: ldloca.s V_0 + IL_000b: callvirt ""void System.Action.Invoke(ref int)"" + IL_0010: ldloc.0 + IL_0011: call ""void System.Console.Write(int)"" + IL_0016: ret +}"); + } + + [Fact] + public void DelegateRefTypeArgument_02() + { + var source = @" +using System; + +C.M1(C.M0); + +class C +{ + public static void M0(int x) + { + x++; + } + + public static void M1(Action> action) + { + int i = 0; + Console.Write(i); + action(ref i); + Console.Write(i); + } +}"; + // PROTOTYPE(delegate-type-args): provide other kinds of restricted type arguments to delegates + var verifier = CompileAndVerify(source, options: TestOptions.UnsafeDebugDll/*, expectedOutput: "01"*/); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("C.M1", @" +{ + // Code size 23 (0x17) + .maxstack 2 + .locals init (int V_0) //i + IL_0000: ldc.i4.0 + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: call ""void System.Console.Write(int)"" + IL_0008: ldarg.0 + IL_0009: ldloca.s V_0 + IL_000b: callvirt ""void System.Action.Invoke(ref int)"" + IL_0010: ldloc.0 + IL_0011: call ""void System.Console.Write(int)"" + IL_0016: ret +}"); + } } } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs index 8961ea40e00a2..6cbca162b2afe 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/DeclarationParsingTests.cs @@ -7884,5 +7884,77 @@ public void DefaultConstraint_04() } EOF(); } + + [Fact] + public void DelegateRefTypeArg() + { + UsingStatement("Action x;"); + + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } + + [Fact] + public void DelegateInTypeArg() + { + UsingStatement("Action x;"); + + N(SyntaxKind.LocalDeclarationStatement); + { + N(SyntaxKind.VariableDeclaration); + { + N(SyntaxKind.GenericName); + { + N(SyntaxKind.IdentifierToken, "Action"); + N(SyntaxKind.TypeArgumentList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.RefType); + { + N(SyntaxKind.RefKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.IntKeyword); + } + } + N(SyntaxKind.GreaterThanToken); + } + } + N(SyntaxKind.VariableDeclarator); + { + N(SyntaxKind.IdentifierToken, "x"); + } + } + N(SyntaxKind.SemicolonToken); + } + EOF(); + } } } diff --git a/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs index 0bf8aa2841a21..acf49aea0d076 100644 --- a/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs +++ b/src/Compilers/Core/Portable/CodeGen/ReferenceDependencyWalker.cs @@ -60,6 +60,13 @@ private static void VisitTypeReference(Cci.ITypeReference typeReference, EmitCon return; } + Cci.IRefTypeReference? refType = typeReference as Cci.IRefTypeReference; + if (refType != null) + { + VisitTypeReference(refType.GetReferencedType(context), context); + return; + } + //Cci.IManagedPointerTypeReference managedPointerType = typeReference as Cci.IManagedPointerTypeReference; //if (managedPointerType != null) //{ diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index 80b96c26c2db7..57c4247ff40f4 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -3725,6 +3725,7 @@ private void SerializeTypeReference(SignatureTypeEncoder encoder, ITypeReference switch (primitiveType) { case PrimitiveTypeCode.Pointer: + case PrimitiveTypeCode.Reference: case PrimitiveTypeCode.FunctionPointer: case PrimitiveTypeCode.NotPrimitive: break; @@ -3741,6 +3742,14 @@ private void SerializeTypeReference(SignatureTypeEncoder encoder, ITypeReference continue; } + if (typeReference is IRefTypeReference refTypeReference) + { + typeReference = refTypeReference.GetReferencedType(Context); + // PROTOTYPE(delegate-type-args): is this right? and can it be provided by S.R.M itself? + encoder.Builder.WriteByte((byte)SignatureTypeCode.ByReference); + continue; + } + if (typeReference is IFunctionPointerTypeReference functionPointerTypeReference) { var signature = functionPointerTypeReference.Signature; diff --git a/src/Compilers/Core/Portable/PEWriter/Types.cs b/src/Compilers/Core/Portable/PEWriter/Types.cs index 97e957df38f1c..ccc3abe6d309f 100644 --- a/src/Compilers/Core/Portable/PEWriter/Types.cs +++ b/src/Compilers/Core/Portable/PEWriter/Types.cs @@ -390,6 +390,17 @@ internal interface IPointerTypeReference : ITypeReference ITypeReference GetTargetType(EmitContext context); } + /// + /// This interface models the metadata representation of a ref to a location in unmanaged memory. + /// + internal interface IRefTypeReference : ITypeReference + { + /// + /// The type of value stored at the target memory location. + /// + ITypeReference GetReferencedType(EmitContext context); + } + /// /// This interface models the metadata representation of a pointer to a function in unmanaged memory. /// diff --git a/src/Compilers/Core/Portable/Symbols/SymbolKind.cs b/src/Compilers/Core/Portable/Symbols/SymbolKind.cs index 795448963f061..4a69b8a8cfe6d 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolKind.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolKind.cs @@ -116,6 +116,11 @@ public enum SymbolKind /// Symbol represents a function pointer type /// FunctionPointerType = 20, + + /// + /// Symbol represents a ref type + /// + RefType = 21, } internal static class SymbolKindInternal diff --git a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs index 0516496a3a2f0..bdcf3bf540ea6 100644 --- a/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs +++ b/src/Compilers/Core/Portable/Symbols/SymbolVisitor.cs @@ -95,6 +95,11 @@ public virtual void VisitFunctionPointerType(IFunctionPointerTypeSymbol symbol) DefaultVisit(symbol); } + public virtual void VisitRefType(ITypeSymbol symbol) + { + DefaultVisit(symbol); + } + public virtual void VisitProperty(IPropertySymbol symbol) { DefaultVisit(symbol); diff --git a/src/Compilers/Core/Portable/Symbols/TypeKind.cs b/src/Compilers/Core/Portable/Symbols/TypeKind.cs index e0e2aec8b5165..bd045dd9353d5 100644 --- a/src/Compilers/Core/Portable/Symbols/TypeKind.cs +++ b/src/Compilers/Core/Portable/Symbols/TypeKind.cs @@ -86,6 +86,11 @@ public enum TypeKind : byte /// Type is a function pointer. /// FunctionPointer = 13, + + /// + /// Type is by-ref to another type. + /// + Ref = 14, } internal static class TypeKindInternal