@@ -109,6 +109,7 @@ private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType,
109109 if ( ImplicitConstantExpressionConversion ( resolveResult , toType ) )
110110 return Conversion . ImplicitConstantExpressionConversion ;
111111 }
112+ // C# 9.0 spec: §10.2.5
112113 if ( resolveResult is InterpolatedStringResolveResult )
113114 {
114115 if ( toType . IsKnownType ( KnownTypeCode . IFormattable ) || toType . IsKnownType ( KnownTypeCode . FormattableString ) )
@@ -122,6 +123,8 @@ private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType,
122123 c = MethodGroupConversion ( resolveResult , toType ) ;
123124 if ( c != Conversion . None )
124125 return c ;
126+ // C# 9.0 spec: §10.2.16 default literal conversions
127+ // TODO
125128 if ( resolveResult . IsCompileTimeConstant )
126129 {
127130 c = StandardImplicitConversion ( resolveResult . Type , toType , allowTuple ) ;
@@ -142,6 +145,7 @@ private Conversion ImplicitConversion(ResolveResult resolveResult, IType toType,
142145 if ( c != Conversion . None )
143146 return c ;
144147 }
148+ // C# 9.0 spec: §10.2.17
145149 if ( resolveResult is ThrowResolveResult )
146150 {
147151 return Conversion . ThrowExpressionConversion ;
@@ -205,7 +209,7 @@ public Conversion StandardImplicitConversion(IType fromType, IType toType)
205209
206210 Conversion StandardImplicitConversion ( IType fromType , IType toType , bool allowTupleConversion )
207211 {
208- // C# 4 .0 spec: §6.3.1
212+ // C# 9 .0 spec: §10.4.2
209213 if ( IdentityConversion ( fromType , toType ) )
210214 return Conversion . IdentityConversion ;
211215 if ( ImplicitNumericConversion ( fromType , toType ) )
@@ -229,6 +233,8 @@ Conversion StandardImplicitConversion(IType fromType, IType toType, bool allowTu
229233 return Conversion . ImplicitPointerConversion ;
230234 if ( allowTupleConversion )
231235 {
236+ // TODO are tuple conversions really standard implicit conversions?
237+ // the C# 9.0 spec doesn't list them as standard implicit conversions.
232238 c = TupleConversion ( fromType , toType , isExplicit : false ) ;
233239 if ( c != Conversion . None )
234240 return c ;
@@ -380,7 +386,7 @@ public bool IdentityConversion(IType fromType, IType toType)
380386
381387 bool ImplicitNumericConversion ( IType fromType , IType toType )
382388 {
383- // C# 4 .0 spec: §6.1.2
389+ // C# 9 .0 spec: §10.2.3
384390
385391 TypeCode from = ReflectionHelper . GetTypeCode ( fromType ) ;
386392 if ( from == TypeCode . Empty )
@@ -448,7 +454,7 @@ bool AnyNumericConversion(IType fromType, IType toType)
448454 #region Enumeration Conversions
449455 Conversion ImplicitEnumerationConversion ( ResolveResult rr , IType toType )
450456 {
451- // C# 4 .0 spec: §6.1.3
457+ // C# 9 .0 spec: §10.2.4 + enum part of §10.2.6 (Nullable conversions)
452458 Debug . Assert ( rr . IsCompileTimeConstant ) ;
453459 TypeCode constantType = ReflectionHelper . GetTypeCode ( rr . Type ) ;
454460 if ( constantType >= TypeCode . SByte && constantType <= TypeCode . Decimal && Convert . ToDouble ( rr . ConstantValue ) == 0 )
@@ -479,7 +485,7 @@ bool ExplicitEnumerationConversion(IType fromType, IType toType)
479485 #region Nullable Conversions
480486 Conversion ImplicitNullableConversion ( IType fromType , IType toType )
481487 {
482- // C# 4 .0 spec: §6.1.4
488+ // C# 9 .0 spec: §10.2.6
483489 if ( NullableType . IsNullable ( toType ) )
484490 {
485491 IType t = NullableType . GetUnderlyingType ( toType ) ;
@@ -513,7 +519,7 @@ Conversion ExplicitNullableConversion(IType fromType, IType toType)
513519 #region Null Literal Conversion
514520 bool NullLiteralConversion ( IType fromType , IType toType )
515521 {
516- // C# 4 .0 spec: §6.1.5
522+ // C# 9 .0 spec: §10.2.7
517523 if ( fromType . Kind == TypeKind . Null )
518524 {
519525 return NullableType . IsNullable ( toType ) || toType . IsReferenceType == true ;
@@ -533,7 +539,7 @@ public bool IsImplicitReferenceConversion(IType fromType, IType toType)
533539
534540 bool ImplicitReferenceConversion ( IType fromType , IType toType , int subtypeCheckNestingDepth )
535541 {
536- // C# 4 .0 spec: §6.1.6
542+ // C# 9 .0 spec: §10.2.8
537543
538544 // reference conversions are possible:
539545 // - if both types are known to be reference types
@@ -552,7 +558,7 @@ bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckN
552558 return fromArray . Dimensions == toArray . Dimensions
553559 && ImplicitReferenceConversion ( fromArray . ElementType , toArray . ElementType , subtypeCheckNestingDepth ) ;
554560 }
555- // conversion from single-dimensional array S[] to IList<T>:
561+ // conversion from single-dimensional array S[] to IList<T>/IReadOnlyList<T> + base interfaces :
556562 IType toTypeArgument = UnpackGenericArrayInterface ( toType ) ;
557563 if ( fromArray . Dimensions == 1 && toTypeArgument != null )
558564 {
@@ -570,20 +576,20 @@ bool ImplicitReferenceConversion(IType fromType, IType toType, int subtypeCheckN
570576 }
571577
572578 /// <summary>
573- /// For IList{T}, ICollection{T}, IEnumerable{T} and IReadOnlyList{T}, returns T.
579+ /// For <see cref=" IList{T}"/>, <see cref=" ICollection{T}"/>, <see cref=" IEnumerable{T}"/> and <see cref=" IReadOnlyList{T}"/> , returns T.
574580 /// Otherwise, returns null.
575581 /// </summary>
576582 IType UnpackGenericArrayInterface ( IType interfaceType )
577583 {
578- ParameterizedType pt = interfaceType as ParameterizedType ;
579- if ( pt != null )
584+ if ( interfaceType is ParameterizedType pt )
580585 {
581586 switch ( pt . GetDefinition ( ) ? . KnownTypeCode )
582587 {
583588 case KnownTypeCode . IListOfT :
584589 case KnownTypeCode . ICollectionOfT :
585590 case KnownTypeCode . IEnumerableOfT :
586591 case KnownTypeCode . IReadOnlyListOfT :
592+ case KnownTypeCode . IReadOnlyCollectionOfT :
587593 return pt . GetTypeArgument ( 0 ) ;
588594 }
589595 }
@@ -780,7 +786,7 @@ bool IsSealedReferenceType(IType type)
780786 #region Boxing Conversions
781787 bool IsBoxingConversion ( IType fromType , IType toType )
782788 {
783- // C# 4 .0 spec: §6.1.7
789+ // C# 9 .0 spec: §10.2.9
784790 fromType = NullableType . GetUnderlyingType ( fromType ) ;
785791 if ( fromType . IsReferenceType == false && ! fromType . IsByRefLike && toType . IsReferenceType == true )
786792 return IsSubtypeOf ( fromType , toType , 0 ) ;
@@ -815,7 +821,7 @@ bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
815821 {
816822 if ( rr == null || ! rr . IsCompileTimeConstant )
817823 return false ;
818- // C# 4 .0 spec: §6.1.9
824+ // C# 9 .0 spec: §10.2.11 + part of §10.2.6 (Nullable conversions)
819825 TypeCode fromTypeCode = ReflectionHelper . GetTypeCode ( rr . Type ) ;
820826 toType = NullableType . GetUnderlyingType ( toType ) ;
821827 TypeCode toTypeCode = ReflectionHelper . GetTypeCode ( toType ) ;
@@ -859,10 +865,11 @@ bool ImplicitConstantExpressionConversion(ResolveResult rr, IType toType)
859865 /// </summary>
860866 bool ImplicitTypeParameterConversion ( IType fromType , IType toType )
861867 {
868+ // C# 9.0 spec: §10.2.12
862869 if ( fromType . Kind != TypeKind . TypeParameter )
863870 return false ; // not a type parameter
864- if ( fromType . IsReferenceType == true )
865- return false ; // already handled by ImplicitReferenceConversion
871+ if ( fromType . IsReferenceType . HasValue )
872+ return false ; // already handled by ImplicitReferenceConversion/BoxingConversion
866873 return IsSubtypeOf ( fromType , toType , 0 ) ;
867874 }
868875
@@ -1212,7 +1219,7 @@ List<OperatorInfo> GetApplicableConversionOperators(ResolveResult fromResult, IT
12121219 #region AnonymousFunctionConversion
12131220 Conversion AnonymousFunctionConversion ( ResolveResult resolveResult , IType toType )
12141221 {
1215- // C# 5 .0 spec §6.5 Anonymous function conversions
1222+ // C# 9 .0 spec §10.7 Anonymous function conversions
12161223 LambdaResolveResult f = resolveResult as LambdaResolveResult ;
12171224 if ( f == null )
12181225 return Conversion . None ;
@@ -1242,6 +1249,7 @@ Conversion AnonymousFunctionConversion(ResolveResult resolveResult, IType toType
12421249 if ( f . IsImplicitlyTyped )
12431250 {
12441251 // If F has an implicitly typed parameter list, D has no ref or out parameters.
1252+ // TODO: what about in parameters?
12451253 foreach ( IParameter p in d . Parameters )
12461254 {
12471255 if ( p . ReferenceKind != ReferenceKind . None )
@@ -1294,9 +1302,8 @@ static IType UnpackExpressionTreeType(IType type)
12941302 #region MethodGroupConversion
12951303 Conversion MethodGroupConversion ( ResolveResult resolveResult , IType toType )
12961304 {
1297- // C# 4.0 spec §6.6 Method group conversions
1298- MethodGroupResolveResult rr = resolveResult as MethodGroupResolveResult ;
1299- if ( rr == null )
1305+ // C# 9.0 spec §10.8 Method group conversions
1306+ if ( resolveResult is not MethodGroupResolveResult rr )
13001307 return Conversion . None ;
13011308 IMethod invoke = toType . GetDelegateInvokeMethod ( ) ;
13021309 if ( invoke == null )
@@ -1312,6 +1319,10 @@ Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType)
13121319 parameterType = ( ( ByReferenceType ) parameterType ) . ElementType ;
13131320 args [ i ] = new ByReferenceResolveResult ( parameterType , param . ReferenceKind ) ;
13141321 }
1322+ else if ( param . Type . Kind == TypeKind . Dynamic )
1323+ {
1324+ args [ i ] = new ResolveResult ( compilation . FindType ( KnownTypeCode . Object ) ) ;
1325+ }
13151326 else
13161327 {
13171328 args [ i ] = new ResolveResult ( parameterType ) ;
@@ -1327,8 +1338,7 @@ Conversion MethodGroupConversion(ResolveResult resolveResult, IType toType)
13271338 if ( or . FoundApplicableCandidate )
13281339 {
13291340 IMethod method = ( IMethod ) or . GetBestCandidateWithSubstitutedTypeArguments ( ) ;
1330- var thisRR = rr . TargetResult as ThisResolveResult ;
1331- bool isVirtual = method . IsOverridable && ! ( thisRR != null && thisRR . CausesNonVirtualInvocation ) ;
1341+ bool isVirtual = method . IsOverridable && ! ( rr . TargetResult is ThisResolveResult { CausesNonVirtualInvocation : true } ) ;
13321342 bool isValid = ! or . IsAmbiguous && IsDelegateCompatible ( method , invoke , or . IsExtensionMethodInvocation ) ;
13331343 bool delegateCapturesFirstArgument = or . IsExtensionMethodInvocation || ! method . IsStatic ;
13341344 if ( isValid )
@@ -1362,32 +1372,33 @@ public bool IsDelegateCompatible(IMethod method, IType delegateType)
13621372
13631373 /// <summary>
13641374 /// Gets whether a method <paramref name="m"/> is compatible with a delegate type.
1365- /// §15.2 Delegate compatibility
13661375 /// </summary>
13671376 /// <param name="m">The method to test for compatibility</param>
1368- /// <param name="invoke ">The invoke method of the delegate</param>
1377+ /// <param name="d ">The invoke method of the delegate</param>
13691378 /// <param name="isExtensionMethodInvocation">Gets whether m is accessed using extension method syntax.
13701379 /// If this parameter is true, the first parameter of <paramref name="m"/> will be ignored.</param>
1371- bool IsDelegateCompatible ( IMethod m , IMethod invoke , bool isExtensionMethodInvocation )
1380+ bool IsDelegateCompatible ( IMethod m , IMethod d , bool isExtensionMethodInvocation )
13721381 {
1382+ // C# 9.0 §20.4 Delegate compatibility
13731383 if ( m == null )
13741384 throw new ArgumentNullException ( nameof ( m ) ) ;
1375- if ( invoke == null )
1376- throw new ArgumentNullException ( nameof ( invoke ) ) ;
1385+ if ( d == null )
1386+ throw new ArgumentNullException ( nameof ( d ) ) ;
13771387 int firstParameterInM = isExtensionMethodInvocation ? 1 : 0 ;
1378- if ( m . Parameters . Count - firstParameterInM != invoke . Parameters . Count )
1388+ if ( m . Parameters . Count - firstParameterInM != d . Parameters . Count )
13791389 return false ;
1380- for ( int i = 0 ; i < invoke . Parameters . Count ; i ++ )
1390+ for ( int i = 0 ; i < d . Parameters . Count ; i ++ )
13811391 {
13821392 var pm = m . Parameters [ firstParameterInM + i ] ;
1383- var pd = invoke . Parameters [ i ] ;
1393+ var pd = d . Parameters [ i ] ;
13841394 // ret/out/in must match
13851395 if ( pm . ReferenceKind != pd . ReferenceKind )
13861396 return false ;
13871397 if ( pm . ReferenceKind != ReferenceKind . None )
13881398 {
13891399 // ref/out/in parameters must have same types
1390- if ( ! pm . Type . Equals ( pd . Type ) )
1400+ // according to the spec, but Roslyn seems to allow identity conversions
1401+ if ( ! IdentityConversion ( pd . Type , pm . Type ) )
13911402 return false ;
13921403 }
13931404 else
@@ -1397,15 +1408,18 @@ bool IsDelegateCompatible(IMethod m, IMethod invoke, bool isExtensionMethodInvoc
13971408 return false ;
13981409 }
13991410 }
1411+ if ( m . ReturnTypeIsRefReadOnly != d . ReturnTypeIsRefReadOnly )
1412+ return false ;
14001413 // check return type compatibility
1401- return IdentityConversion ( m . ReturnType , invoke . ReturnType )
1402- || IsImplicitReferenceConversion ( m . ReturnType , invoke . ReturnType ) ;
1414+ return IdentityConversion ( m . ReturnType , d . ReturnType )
1415+ || IsImplicitReferenceConversion ( m . ReturnType , d . ReturnType ) ;
14031416 }
14041417 #endregion
14051418
14061419 #region Tuple Conversion
14071420 Conversion TupleConversion ( TupleResolveResult fromRR , IType toType , bool isExplicit )
14081421 {
1422+ // C# 9.0 spec: §10.2.13 (implicit tuple conversions) + $10.3.6 (explicit tuple conversions)
14091423 var fromElements = fromRR . Elements ;
14101424 var toElements = TupleType . GetTupleElementTypes ( toType ) ;
14111425 if ( toElements . IsDefault || fromElements . Length != toElements . Length )
@@ -1431,6 +1445,7 @@ Conversion TupleConversion(TupleResolveResult fromRR, IType toType, bool isExpli
14311445
14321446 Conversion TupleConversion ( IType fromType , IType toType , bool isExplicit )
14331447 {
1448+ // C# 9.0 spec: §10.2.13 (implicit tuple conversions) + $10.3.6 (explicit tuple conversions)
14341449 var fromElements = TupleType . GetTupleElementTypes ( fromType ) ;
14351450 if ( fromElements . IsDefaultOrEmpty )
14361451 return Conversion . None ;
0 commit comments