@@ -1943,11 +1943,7 @@ private static void GetInvocationArgumentsForEscape(
19431943 parameters [ argsToParamsOpt . IsDefault ? argIndex : argsToParamsOpt [ argIndex ] ] :
19441944 null ;
19451945
1946- if ( mixableArguments is not null
1947- && isMixableParameter ( parameter )
1948- // assume any expression variable is a valid mixing destination,
1949- // since we will infer a legal val-escape for it (if it doesn't already have a narrower one).
1950- && isMixableArgument ( argument ) )
1946+ if ( mixableArguments is not null && isMixableParameter ( parameter ) )
19511947 {
19521948 mixableArguments . Add ( new MixableDestination ( parameter , argument ) ) ;
19531949 }
@@ -1972,9 +1968,6 @@ parameter is not null &&
19721968 parameter . Type . IsRefLikeType &&
19731969 parameter . RefKind . IsWritableReference ( ) ;
19741970
1975- static bool isMixableArgument ( BoundExpression argument ) =>
1976- argument is not ( BoundDeconstructValuePlaceholder or BoundLocal { DeclarationKind : not BoundLocalDeclarationKind . None } ) ;
1977-
19781971 static EscapeArgument getReceiver ( Symbol symbol , BoundExpression receiver )
19791972 {
19801973 if ( symbol is FunctionPointerMethodSymbol )
@@ -2202,30 +2195,6 @@ private bool UseUpdatedEscapeRulesForInvocation(Symbol symbol)
22022195 return method ? . UseUpdatedEscapeRules == true ;
22032196 }
22042197
2205- private static bool ShouldInferDeclarationExpressionValEscape ( BoundExpression argument , [ NotNullWhen ( true ) ] out SourceLocalSymbol ? localSymbol )
2206- {
2207- var symbol = argument switch
2208- {
2209- BoundDeconstructValuePlaceholder p => p . ExpressionSymbol ,
2210- BoundLocal { DeclarationKind : not BoundLocalDeclarationKind . None } l => l . ExpressionSymbol ,
2211- _ => null
2212- } ;
2213- if ( symbol is SourceLocalSymbol { ValEscapeScope : CallingMethodScope } local )
2214- {
2215- localSymbol = local ;
2216- return true ;
2217- }
2218- else
2219- {
2220- // No need to infer a val escape for a global variable.
2221- // These are only used in top-level statements in scripting mode,
2222- // and since they are class fields, their scope is always CallingMethod.
2223- Debug . Assert ( symbol is null or SourceLocalSymbol or GlobalExpressionVariable ) ;
2224- localSymbol = null ;
2225- return false ;
2226- }
2227- }
2228-
22292198 /// <summary>
22302199 /// Validates whether the invocation is valid per no-mixing rules.
22312200 /// Returns <see langword="false"/> when it is not valid and produces diagnostics (possibly more than one recursively) that helps to figure the reason.
@@ -2270,7 +2239,7 @@ private bool CheckInvocationArgMixing(
22702239 var escapeArguments = ArrayBuilder < EscapeArgument > . GetInstance ( ) ;
22712240 GetInvocationArgumentsForEscape (
22722241 symbol ,
2273- receiverOpt ,
2242+ receiver : null , // receiver handled explicitly below
22742243 parameters ,
22752244 argsOpt ,
22762245 argRefKindsOpt : default ,
@@ -2283,51 +2252,43 @@ private bool CheckInvocationArgMixing(
22832252 {
22842253 foreach ( var ( _, argument , refKind ) in escapeArguments )
22852254 {
2286- if ( ShouldInferDeclarationExpressionValEscape ( argument , out _ ) )
2287- {
2288- // assume any expression variable is a valid mixing destination,
2289- // since we will infer a legal val-escape for it (if it doesn't already have a narrower one).
2290- continue ;
2291- }
2292-
22932255 if ( refKind . IsWritableReference ( ) && argument . Type ? . IsRefLikeType == true )
22942256 {
22952257 escapeTo = Math . Min ( escapeTo , GetValEscape ( argument , scopeOfTheContainingExpression ) ) ;
22962258 }
22972259 }
22982260
2299- var hasMixingError = false ;
2261+ if ( escapeTo == scopeOfTheContainingExpression )
2262+ {
2263+ // cannot fail. common case.
2264+ return true ;
2265+ }
23002266
2301- // track the widest scope that arguments could safely escape to.
2302- // use this scope as the inferred STE of declaration expressions.
2303- var inferredDestinationValEscape = CallingMethodScope ;
23042267 foreach ( var ( parameter , argument , _) in escapeArguments )
23052268 {
2306- // in the old rules, we assume that refs cannot escape into ref struct variables.
2307- // e.g. in `dest = M(ref arg)`, we assume `ref arg` will not escape into `dest`, but `arg` might.
2308- inferredDestinationValEscape = Math . Max ( inferredDestinationValEscape , GetValEscape ( argument , scopeOfTheContainingExpression ) ) ;
2309- if ( ! hasMixingError && ! CheckValEscape ( argument . Syntax , argument , scopeOfTheContainingExpression , escapeTo , false , diagnostics ) )
2269+ var valid = CheckValEscape ( argument . Syntax , argument , scopeOfTheContainingExpression , escapeTo , false , diagnostics ) ;
2270+
2271+ if ( ! valid )
23102272 {
23112273 string parameterName = GetInvocationParameterName ( parameter ) ;
23122274 Error ( diagnostics , ErrorCode . ERR_CallArgMixing , syntax , symbol , parameterName ) ;
2313- hasMixingError = true ;
2314- }
2315- }
2316-
2317- foreach ( var ( _, argument , _) in escapeArguments )
2318- {
2319- if ( ShouldInferDeclarationExpressionValEscape ( argument , out var localSymbol ) )
2320- {
2321- localSymbol . SetValEscape ( inferredDestinationValEscape ) ;
2275+ return false ;
23222276 }
23232277 }
2324-
2325- return ! hasMixingError ;
23262278 }
23272279 finally
23282280 {
23292281 escapeArguments . Free ( ) ;
23302282 }
2283+
2284+ // check val escape of receiver if ref-like
2285+ if ( receiverOpt ? . Type ? . IsRefLikeType == true )
2286+ {
2287+ // Should we also report ErrorCode.ERR_CallArgMixing if CheckValEscape() fails?
2288+ return CheckValEscape ( receiverOpt . Syntax , receiverOpt , scopeOfTheContainingExpression , escapeTo , false , diagnostics ) ;
2289+ }
2290+
2291+ return true ;
23312292 }
23322293
23332294 private bool CheckInvocationArgMixingWithUpdatedRules (
@@ -2391,32 +2352,9 @@ private bool CheckInvocationArgMixingWithUpdatedRules(
23912352 }
23922353 }
23932354
2394- inferDeclarationExpressionValEscape ( ) ;
2395-
23962355 mixableArguments . Free ( ) ;
23972356 escapeValues . Free ( ) ;
23982357 return valid ;
2399-
2400- void inferDeclarationExpressionValEscape ( )
2401- {
2402- // find the widest scope that arguments could safely escape to.
2403- // use this scope as the inferred STE of declaration expressions.
2404- var inferredDestinationValEscape = CallingMethodScope ;
2405- foreach ( var ( _, fromArg , _, isRefEscape ) in escapeValues )
2406- {
2407- inferredDestinationValEscape = Math . Max ( inferredDestinationValEscape , isRefEscape
2408- ? GetRefEscape ( fromArg , scopeOfTheContainingExpression )
2409- : GetValEscape ( fromArg , scopeOfTheContainingExpression ) ) ;
2410- }
2411-
2412- foreach ( var ( _, fromArg , _, _) in escapeValues )
2413- {
2414- if ( ShouldInferDeclarationExpressionValEscape ( fromArg , out var localSymbol ) )
2415- {
2416- localSymbol . SetValEscape ( inferredDestinationValEscape ) ;
2417- }
2418- }
2419- }
24202358 }
24212359
24222360 private static bool IsReceiverRefReadOnly ( Symbol methodOrPropertySymbol ) => methodOrPropertySymbol switch
0 commit comments