-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Remove the bool IsUnsafe(this TypeSymbol type)
method
#67831
Conversation
@@ -223,7 +223,7 @@ private TypeSymbol GetAnonymousTypeFieldType(BoundExpression expression, CSharpS | |||
errorArg = expressionType; | |||
expressionType = CreateErrorType(SyntaxFacts.GetText(SyntaxKind.VoidKeyword)); | |||
} | |||
else if (expressionType.IsUnsafe()) |
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.
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.
We get here with void*
with a scenario like:
static void* M(void* b)
{
var a = new { F = b };
return a.F;
}
That scenario remains an error (it's a pointer type).
On the other hand, scenarios with void*[]
are now allowed:
var a = new { F = new void*[0] };
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.
might be worthwhile to have a comment to this effect.
@@ -1469,7 +1469,8 @@ static ErrorCode getRefMismatchErrorCode(TypeKind type) | |||
return true; | |||
} | |||
|
|||
if ((selectedMethod.HasUnsafeParameter() || selectedMethod.ReturnType.IsUnsafe()) && ReportUnsafeIfNotAllowed(syntax, diagnostics)) | |||
if ((selectedMethod.HasPointerOrFunctionPointerParameterType() || selectedMethod.ReturnType.IsPointerOrFunctionPointer()) |
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.
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.
Those were used. See the UnsafeDelegateAssignment_*
tests. The scenarios involving arrays of pointers were previously reported as error, but no longer are.
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.
Are we testing constructed types such as delegate List<int*[]> D();
? It looks like those cases should result in errors now.
4198a4d
to
a69f097
Compare
@cston @dotnet/roslyn-compiler Please take a look. Thanks |
{ | ||
if (!expr.HasAnyErrors && !IsInsideNameof) | ||
{ | ||
TypeSymbol exprType = expr.Type; | ||
if ((object)exprType != null && exprType.IsUnsafe()) | ||
if ((object)exprType != null && exprType.ContainsPointer()) |
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.
Nit. "IsOrContainsPointerType" feels like a clearer name to me. But i can accept ContainsPointer as being clear enough as well. #Resolved
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.
I'll keep ContainsPointer
because that name aligns with other methods we have, like ContainsDynamic
, ContainsNativeIntegerWrapperType
, ContainsErrorType
, etc
return false; | ||
} | ||
} | ||
} |
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.
👍 #Closed
@cston @dotnet/roslyn-compiler Please take a look. Thanks |
Diagnostic(ErrorCode.ERR_UnsafeNeeded, "B<delegate*<void>[]>").WithLocation(12, 12), | ||
// (12,12): error CS0214: Pointers and fixed size buffers may only be used in an unsafe context | ||
// [A<object>(B<delegate*<void>[]>.C)] | ||
Diagnostic(ErrorCode.ERR_UnsafeNeeded, "B<delegate*<void>[]>.C").WithLocation(12, 12), |
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.
Just curious: Why are we reporting two errors for the type? #Resolved
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.
The diagnostic for delegate*
is reported in BindNamespaceOrTypeOrAliasSymbol
(part of BindType
).
The diagnostic for B<delegate*<void>[]>
is reported in BindExpression
(because it's on the left of a member access).
The diagnostic for B<delegate*<void>[]>.C
is reported in BindExpression
.
} | ||
|
||
[Fact] | ||
public void ConstructorInitializerWithUnsafeArgument_PointerArray_Unsafe() |
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.
} | ||
|
||
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67330")] | ||
public void AnonymousTypeSymbols_PointerListField() |
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.
return expr; | ||
} | ||
|
||
private void VerifyUnchecked(ExpressionSyntax node, BindingDiagnosticBag diagnostics, BoundExpression expr) | ||
private void CheckContextForPointerTypes(ExpressionSyntax node, BindingDiagnosticBag diagnostics, BoundExpression expr) | ||
{ | ||
if (!expr.HasAnyErrors && !IsInsideNameof) |
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.
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.
Possibly. That seems a good idea but I'm trying to keep this PR as small/focused as possible.
Should we adjust the wording of the message since we are relaxing the condition? In reply to: 1540837450 In reply to: 1540837450 Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs:742 in 9eb0804. [](commit_id = 9eb0804, deletion_comment = True) |
Should we adjust the wording of this message? In reply to: 1540840295 Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:965 in 9eb0804. [](commit_id = 9eb0804, deletion_comment = True) |
@@ -113,7 +113,7 @@ public static void ValidateIteratorMethod(CSharpCompilation compilation, MethodS | |||
{ | |||
diagnostics.Add(ErrorCode.ERR_BadIteratorArgType, parameter.GetFirstLocation()); | |||
} | |||
else if (parameter.Type.IsUnsafe()) | |||
else if (parameter.Type.IsPointerOrFunctionPointer()) | |||
{ | |||
diagnostics.Add(ErrorCode.ERR_UnsafeIteratorArgType, parameter.GetFirstLocation()); |
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.
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.
This question is relevant if we decide to adjust the wording of the message.
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.
For iterators, yielding a pointer type is indeed forbidden (pointer types cannot be used as type arguments, guarded with normal "error CS0306: The type '...' may not be used as a type argument").
I agree that this error message doesn't need to mention yielded types, but it also seems fine to leave as-is (the message is not wrong) and it's tangential to this PR.
@@ -66,7 +66,7 @@ internal void ReportAsyncParameterErrors(BindingDiagnosticBag diagnostics, Locat | |||
{ | |||
diagnostics.Add(ErrorCode.ERR_BadAsyncArgType, getLocation(parameter, location)); | |||
} | |||
else if (parameter.Type.IsUnsafe()) | |||
else if (parameter.Type.IsPointerOrFunctionPointer()) | |||
{ | |||
diagnostics.Add(ErrorCode.ERR_UnsafeAsyncArgType, getLocation(parameter, location)); |
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.
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.
This question is relevant if we decide to adjust the wording of the message.
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.
The same applies here. Task/ValueTask/MyTask<pointer>
is forbidden because of type argument restrictions. The message doesn't need to mention return types and arguably could be better if it didn't, but I don't plan to change it.
{ | ||
var references = new[] { reference0, reference1 }; | ||
AssertUsedAssemblyReferences(source, references, references, parseOptions: TestOptions.Regular11); |
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.
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.
I guess, it is fine to leave as is if the test below does exactly that.
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.
That was my feeling too
} | ||
"""; | ||
var comp = CreateCompilation(source, options: TestOptions.UnsafeDebugDll); | ||
comp.VerifyDiagnostics(); |
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.
Done with review pass (commit 8) |
I didn't follow. The PR tightens the enforcement that pointer types do not exist outside of unsafe context (we're reporting more instances of ERR_UnsafeNeeded than we did before). In reply to: 1540837450 Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs:742 in 9eb0804. [](commit_id = 9eb0804, deletion_comment = True) |
That code didn't change and the error still seems correct. I couldn't dig the spec referenced in our code, but this PR doesn't aim to change the rule we use:
I'll add a note of clarification to description in OP (pointer scenarios remain disallowed in iterators because their context is always safe). In reply to: 1540840295 Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/UnsafeTests.cs:965 in 9eb0804. [](commit_id = 9eb0804, deletion_comment = True) |
Sorry, it looks like CodeFlow failed to place the comment properly. I you look at the diff between commits 1 and 9 for test
It looks like we relaxed condition for ERR_UnsafeAsyncArgType. The term "unsafe parameters or return types" now feels misleading. Perhaps we should say "pointer types" instead. In reply to: 1541163321 Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/BindingAsyncTests.cs:742 in 9eb0804. [](commit_id = 9eb0804, deletion_comment = True) |
@@ -944,6 +950,44 @@ public void UnsafeIteratorSignatures() | |||
Diagnostic(ErrorCode.ERR_IllegalInnerUnsafe, "Iterator")); //this is for putting "unsafe" on an iterator, not for the parameter type | |||
} | |||
|
|||
[Fact] | |||
public void UnsafeIteratorSignatures_PointerArray() |
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.
The other comment was placed by CodeFlow in a wrong place. In the test UnsafeIteratorSignatures_PointerArray
(commit 1 vs. 9), we are removing:
// (4,66): error CS1637: Iterators cannot have unsafe parameters or yield types
// System.Collections.Generic.IEnumerable<int> Iterator(int*[] p)
Diagnostic(ErrorCode.ERR_UnsafeIteratorArgType, "p").WithLocation(4, 66)
Perhaps the unsafe
is a misleading term to use in the message now. The check is specifically about pointer types now, and unsafe
types in a more general term are not disallowed. #Closed
Thanks for the clarification. Updated the uses of "unsafe" in those two diagnostics. |
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.
LGTM (commit 10), assuming CI is passing
Fixes #67330
The first commit adds some tests as baseline (to show the impact of the change). This will be squashed, so you can ignore the
TODO2
comments in that first commit :-PThe compiler had this confused helper method “IsUnsafe” which groups pointers, function pointers and arrays thereof together as “unsafe”.
It’s confused because either we only care about nested pointer types or we don’t, but this method cares about pointer arrays but misses a slew of nested scenarios (such as
List<int*[]>
).I reviewed the places where it’s used and determined which should be relaxed (only care about top-level pointer types) and which should be tightened (to care about all nested pointer types).
There are two buckets:
a. Usings (which ignores pointer types before C# 12)
b. Invocations/new/base (reports any “unsafe” parameter type outside an unsafe context)
c. Method group conversions:
D d = M;
(fails if involves an “unsafe” type outside an unsafe context)d. lambda parameter types:
D d = (x) => x;
(reports “unsafe” parameter types outside an unsafe context)e. referencing an alias type
f. expressions
a. anonymous types, records (members disallowed if “unsafe” type)
b. async/iterators (parameters disallowed if “unsafe” type)
This PR makes the following changes:
using
(per recent LDM discussion, because there was no way to declare an unsafe using)Update: note that unsafe code will remain disallowed in iterators ("An iterator block always defines a safe context, even when its declaration is nested in an unsafe context." is quoted in the code)