-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Extensible Fixed Statement #24469
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
Extensible Fixed Statement #24469
Changes from all commits
0c41f1d
7cb61a2
3ff7674
2b88c1d
3097ec9
9ab10c2
f2a1db6
d810de6
276da81
528b171
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1041,102 +1041,190 @@ private bool IsValidFixedVariableInitializer(TypeSymbol declType, SourceLocalSym | |
Debug.Assert(!ReferenceEquals(declType, null)); | ||
Debug.Assert(declType.IsPointerType()); | ||
|
||
if (ReferenceEquals(initializerOpt, null)) | ||
if (initializerOpt?.HasAnyErrors != false) | ||
{ | ||
return false; | ||
} | ||
|
||
TypeSymbol initializerType = initializerOpt.Type; | ||
SyntaxNode initializerSyntax = initializerOpt.Syntax; | ||
|
||
if (ReferenceEquals(initializerType, null)) | ||
if ((object)initializerType == null) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In reply to: 167688236 [](ancestors = 167688236) |
||
{ | ||
initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics); | ||
if (!initializerOpt.HasAnyErrors) | ||
{ | ||
// Dev10 just reports the assignment conversion error, which must occur, except for these cases: | ||
Debug.Assert( | ||
initializerOpt is BoundConvertedStackAllocExpression || | ||
initializerOpt is BoundConversion conversion && (conversion.Operand.IsLiteralNull() || conversion.Operand.Kind == BoundKind.DefaultExpression), | ||
"All other typeless expressions should have conversion errors"); | ||
Error(diagnostics, ErrorCode.ERR_ExprCannotBeFixed, initializerSyntax); | ||
return false; | ||
} | ||
|
||
// CONSIDER: this is a very confusing error message, but it's what Dev10 reports. | ||
Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); | ||
} | ||
TypeSymbol elementType; | ||
bool hasErrors = false; | ||
MethodSymbol getPinnableMethod = null; | ||
|
||
switch (initializerOpt.Kind) | ||
{ | ||
case BoundKind.AddressOfOperator: | ||
elementType = ((BoundAddressOfOperator)initializerOpt).Operand.Type; | ||
break; | ||
|
||
case BoundKind.FieldAccess: | ||
var fa = (BoundFieldAccess)initializerOpt; | ||
if (fa.FieldSymbol.IsFixed) | ||
{ | ||
elementType = ((PointerTypeSymbol)fa.Type).PointedAtType; | ||
break; | ||
} | ||
|
||
goto default; | ||
|
||
default: | ||
// fixed (T* variable = <expr>) ... | ||
|
||
// check for arrays | ||
if (initializerType.IsArray()) | ||
{ | ||
// See ExpressionBinder::BindPtrToArray (though most of that functionality is now in LocalRewriter). | ||
elementType = ((ArrayTypeSymbol)initializerType).ElementType; | ||
break; | ||
} | ||
|
||
// check for a special ref-returning method | ||
var additionalDiagnostics = DiagnosticBag.GetInstance(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What was the motivation for removing the try/catch for freeing this instance? #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Andy's comment on this PR. I was undecided which way is better, so if someone also thinks that try/finally makes this more complex than needed, I've removed it In reply to: 169487211 [](ancestors = 169487211) |
||
getPinnableMethod = GetDangerousGetPinnableReferenceMethodOpt(initializerOpt, additionalDiagnostics); | ||
|
||
// check for String | ||
// NOTE: We will allow DangerousGetPinnableReferenceMethod to take precendence, but only if it is a member of System.String | ||
if (initializerType.SpecialType == SpecialType.System_String && | ||
((object)getPinnableMethod == null || getPinnableMethod.ContainingType.SpecialType != SpecialType.System_String)) | ||
{ | ||
elementType = this.GetSpecialType(SpecialType.System_Char, diagnostics, initializerSyntax); | ||
additionalDiagnostics.Free(); | ||
break; | ||
} | ||
|
||
if ((object)getPinnableMethod != null) | ||
{ | ||
elementType = getPinnableMethod.ReturnType; | ||
CheckFeatureAvailability(initializerOpt.Syntax, MessageID.IDS_FeatureExtensibleFixedStatement, diagnostics); | ||
additionalDiagnostics.Free(); | ||
break; | ||
} | ||
else | ||
{ | ||
// if the feature was enabled but something went wrong with the method, report that | ||
bool extensibleFixedEnabled = ((CSharpParseOptions)initializerOpt.SyntaxTree.Options)?.IsFeatureEnabled(MessageID.IDS_FeatureExtensibleFixedStatement) != false; | ||
if (extensibleFixedEnabled && additionalDiagnostics != null) | ||
{ | ||
diagnostics.AddRange(additionalDiagnostics); | ||
} | ||
Error(diagnostics, ErrorCode.ERR_ExprCannotBeFixed, initializerSyntax); | ||
additionalDiagnostics.Free(); | ||
return false; | ||
} | ||
} | ||
else | ||
|
||
if (elementType.IsManagedType) | ||
{ | ||
TypeSymbol elementType; | ||
bool hasErrors = false; | ||
Error(diagnostics, ErrorCode.ERR_ManagedAddr, initializerSyntax, elementType); | ||
hasErrors = true; | ||
} | ||
|
||
if (initializerType.SpecialType == SpecialType.System_String) | ||
initializerOpt = GetFixedLocalCollectionInitializer(initializerOpt, elementType, declType, getPinnableMethod, hasErrors, diagnostics); | ||
return true; | ||
} | ||
|
||
private MethodSymbol GetDangerousGetPinnableReferenceMethodOpt(BoundExpression initializer, DiagnosticBag additionalDiagnostics) | ||
{ | ||
if (initializer.Type.SpecialType == SpecialType.System_Void) | ||
{ | ||
return null; | ||
} | ||
|
||
const string methodName = "DangerousGetPinnableReference"; | ||
|
||
DiagnosticBag bindingDiagnostics = DiagnosticBag.GetInstance(); | ||
try | ||
{ | ||
var boundAccess = BindInstanceMemberAccess( | ||
initializer.Syntax, | ||
initializer.Syntax, | ||
initializer, | ||
methodName, | ||
rightArity: 0, | ||
typeArgumentsSyntax: default, | ||
typeArguments: default, | ||
invoked: true, | ||
bindingDiagnostics); | ||
|
||
if (boundAccess.Kind != BoundKind.MethodGroup) | ||
{ | ||
elementType = this.GetSpecialType(SpecialType.System_Char, diagnostics, initializerSyntax); | ||
Debug.Assert(!elementType.IsManagedType); | ||
// the thing is not even a method | ||
return null; | ||
} | ||
else if (initializerType.IsArray()) | ||
|
||
var analyzedArguments = AnalyzedArguments.GetInstance(); | ||
BoundExpression getPinnableReferenceCall = BindMethodGroupInvocation( | ||
initializer.Syntax, | ||
initializer.Syntax, | ||
methodName, | ||
(BoundMethodGroup)boundAccess, | ||
analyzedArguments, | ||
bindingDiagnostics, | ||
queryClause: null, | ||
allowUnexpandedForm: false); | ||
|
||
analyzedArguments.Free(); | ||
|
||
if (getPinnableReferenceCall.Kind != BoundKind.Call) | ||
{ | ||
// See ExpressionBinder::BindPtrToArray (though most of that functionality is now in LocalRewriter). | ||
var arrayType = (ArrayTypeSymbol)initializerType; | ||
elementType = arrayType.ElementType; | ||
// did not find anything callable | ||
return null; | ||
} | ||
|
||
if (elementType.IsManagedType) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_ManagedAddr, initializerSyntax, elementType); | ||
hasErrors = true; | ||
} | ||
var call = (BoundCall)getPinnableReferenceCall; | ||
if (call.ResultKind == LookupResultKind.Empty) | ||
{ | ||
// did not find any methods that even remotely fit | ||
return null; | ||
} | ||
else if (!initializerOpt.HasAnyErrors) | ||
|
||
var getPinnableReferenceMethod = call.Method; | ||
if (getPinnableReferenceMethod is ErrorMethodSymbol || | ||
getPinnableReferenceCall.HasAnyErrors) | ||
{ | ||
elementType = initializerOpt.Type; | ||
switch (initializerOpt.Kind) | ||
{ | ||
case BoundKind.AddressOfOperator: | ||
elementType = ((BoundAddressOfOperator)initializerOpt).Operand.Type; | ||
break; | ||
case BoundKind.FieldAccess: | ||
var fa = (BoundFieldAccess)initializerOpt; | ||
if (!fa.FieldSymbol.IsFixed) | ||
{ | ||
Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); | ||
return true; | ||
} | ||
else | ||
{ | ||
elementType = ((PointerTypeSymbol)fa.Type).PointedAtType; | ||
} | ||
break; | ||
case BoundKind.Conversion: | ||
// The following assertion would not be correct because there might be an implicit conversion after (above) the explicit one. | ||
//Debug.Assert(((BoundConversion)initializerOpt).ExplicitCastInCode, "The assignment conversion hasn't been applied yet, so this must be from source."); | ||
|
||
// NOTE: Dev10 specifically doesn't report this error for the array or string cases. | ||
Error(diagnostics, ErrorCode.ERR_BadCastInFixed, initializerSyntax); | ||
return true; | ||
default: | ||
// CONSIDER: this is a very confusing error message, but it's what Dev10 reports. | ||
Error(diagnostics, ErrorCode.ERR_FixedNotNeeded, initializerSyntax); | ||
return true; | ||
} | ||
// we almost succeded, this is unusual and may be hard to diagnose. | ||
// report additional errors on why we failed to bind the helper | ||
additionalDiagnostics.AddRange(bindingDiagnostics); | ||
return null; | ||
} | ||
else | ||
|
||
if (HasOptionalOrVariableParameters(getPinnableReferenceMethod) || | ||
getPinnableReferenceMethod.ReturnsVoid || | ||
!getPinnableReferenceMethod.RefKind.IsManagedReference() || | ||
!(getPinnableReferenceMethod.ParameterCount == 0 || getPinnableReferenceMethod.IsStatic && getPinnableReferenceMethod.ParameterCount == 1)) | ||
{ | ||
// convert to generate any additional conversion errors | ||
initializerOpt = GenerateConversionForAssignment(declType, initializerOpt, diagnostics); | ||
return true; | ||
// the method does not fit the pattern | ||
additionalDiagnostics.Add(ErrorCode.WRN_PatternBadSignature, initializer.Syntax.Location, initializer.Type, "fixed", getPinnableReferenceMethod); | ||
return null; | ||
} | ||
|
||
initializerOpt = GetFixedLocalCollectionInitializer(initializerOpt, elementType, declType, hasErrors, diagnostics); | ||
return getPinnableReferenceMethod; | ||
} | ||
finally | ||
{ | ||
bindingDiagnostics.Free(); | ||
} | ||
|
||
return true; | ||
} | ||
|
||
/// <summary> | ||
/// Wrap the initializer in a BoundFixedLocalCollectionInitializer so that the rewriter will have the | ||
/// information it needs (e.g. conversions, helper methods). | ||
/// </summary> | ||
private BoundExpression GetFixedLocalCollectionInitializer(BoundExpression initializer, TypeSymbol elementType, TypeSymbol declType, bool hasErrors, DiagnosticBag diagnostics) | ||
private BoundExpression GetFixedLocalCollectionInitializer( | ||
BoundExpression initializer, | ||
TypeSymbol elementType, | ||
TypeSymbol declType, | ||
MethodSymbol getPinnableMethodOpt, | ||
bool hasErrors, | ||
DiagnosticBag diagnostics) | ||
{ | ||
Debug.Assert(initializer != null); | ||
|
||
|
@@ -1158,6 +1246,7 @@ private BoundExpression GetFixedLocalCollectionInitializer(BoundExpression initi | |
pointerType, | ||
elementConversion, | ||
initializer, | ||
getPinnableMethodOpt, | ||
declType, | ||
hasErrors); | ||
} | ||
|
This file was deleted.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does a test cover this path? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a test
In reply to: 167654777 [](ancestors = 167654777)