Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
ce663db
Merge pull request #58411 from dotnet/merges/main-to-features/require…
dotnet-bot Dec 19, 2021
e8598a4
Merge pull request #58493 from dotnet/merges/main-to-features/require…
dotnet-bot Dec 26, 2021
b56a89c
Merge pull request #58545 from dotnet/merges/main-to-features/require…
dotnet-bot Jan 2, 2022
d019178
Merge pull request #58735 from dotnet/merges/main-to-features/require…
dotnet-bot Jan 9, 2022
8212481
Required modifier parsing (#58431)
333fred Jan 10, 2022
3004d67
Support declaration and emit of `required` members in source (#58507)
333fred Feb 4, 2022
4adfb65
Add support for decoding required members in metadata (#59288)
333fred Feb 15, 2022
a487604
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred Feb 15, 2022
c4c397e
Merge pull request #58883 from dotnet/merges/main-to-features/require…
dotnet-bot Feb 15, 2022
3ab340e
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred Feb 28, 2022
e676fe9
Merge pull request #59665 from dotnet/merges/main-to-features/require…
333fred Mar 1, 2022
1fd3098
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred Mar 7, 2022
44f9669
Merge pull request #59971 from dotnet/merges/main-to-features/require…
dotnet-bot Mar 15, 2022
47c81fe
Error when required members are not set (#60101)
333fred Mar 17, 2022
9cfe9e1
Merge pull request #60279 from dotnet/merges/main-to-features/require…
dotnet-bot Mar 20, 2022
4804991
Merge main to features/required-members (#60373)
333fred Mar 25, 2022
f2d1b99
Suppress nullable property/field initialization warning for required …
333fred Mar 25, 2022
84c82b3
Merge pull request #60407 from dotnet/merges/main-to-features/require…
dotnet-bot Mar 27, 2022
4d60bab
Merge pull request #60541 from dotnet/merges/main-to-features/require…
dotnet-bot Apr 3, 2022
c4350a0
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred Apr 11, 2022
32bb9f6
Update for definite assignment changes
333fred Apr 11, 2022
b0798f4
Merge main to features/required-members (#60674)
333fred Apr 11, 2022
16a02f8
Merge remote-tracking branch 'upstream/main' into merge-main-to-featu…
333fred Apr 14, 2022
77dba9e
Merge pull request #60760 from dotnet/merge-main-to-features/required…
333fred Apr 15, 2022
53ce8d4
Support SetsRequiredMembersAttribute (#60392)
333fred Apr 15, 2022
23ccd9d
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred Apr 26, 2022
238919d
Merge pull request #60797 from dotnet/merges/main-to-features/require…
dotnet-bot Apr 26, 2022
dbca19f
Forbid types with required members from substituting for where T : ne…
333fred Apr 27, 2022
c803634
Emit ObsoleteAttribute for required member constructors (#61005)
333fred Apr 28, 2022
28b82fe
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred May 4, 2022
e8928fd
Update tests after merge
333fred May 4, 2022
221aebe
Merge pull request #61068 from dotnet/merges/main-to-features/require…
dotnet-bot May 4, 2022
9b63c76
Merge pull request #61328 from dotnet/merges/main-to-features/require…
dotnet-bot May 15, 2022
9d1754a
Add support for CompilerFeatureRequiredAttribute (#61113)
333fred May 18, 2022
59324d0
Emit CompilerFeatureRequired for ref structs when present.
333fred May 17, 2022
cb14f67
Support emitting CompilerFeatureRequiredAttribute for contructors of …
333fred May 18, 2022
e9c411e
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred May 23, 2022
176f71b
Merge pull request #61447 from dotnet/merges/main-to-features/require…
dotnet-bot May 23, 2022
7c0a0e5
Address prototype comments (#61436)
333fred May 24, 2022
8452ec2
Final prototype comments and top level statements local adjustments (…
333fred May 27, 2022
ba92379
IDE Support for Required Members (#61440)
333fred May 27, 2022
91b259d
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred May 27, 2022
087e571
Merge pull request #61565 from 333fred/merges/main-to-features/requir…
333fred May 27, 2022
a555cad
Merge remote-tracking branch 'upstream/main' into merges/main-to-feat…
333fred May 27, 2022
ce12420
Change natural type of UTF-8 string literals to `ReadOnlySpan<byte>` …
333fred May 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,16 @@ Console.WriteLine($"{{{12:X}}}");
The workaround is to remove the extra braces in the format string.

You can learn more about this change in the associated [roslyn issue](https://github.com/dotnet/roslyn/issues/57750).

## Types cannot be named `required`

***Introduced in Visual Studio 2022 version 17.3.*** Starting in C# 11, types cannot be named `required`. The compiler will report an error on all such type names. To work around this, the type name and all usages must be escaped with an `@`:

```csharp
class required {} // Error CS9029
class @required {} // No error
```

This was done as `required` is now a member modifier for properties and fields.

You can learn more about this change in the associated [csharplang issue](https://github.com/dotnet/csharplang/issues/3630).
5 changes: 3 additions & 2 deletions docs/contributing/Compiler Test Plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ This document provides guidance for thinking about language interactions and tes
- Access modifiers (public, protected, internal, protected internal, private protected, private), static, ref
- type declarations (class, record class/struct with or without positional members, struct, interface, type parameter)
- methods
- fields
- properties (including get/set/init accessors)
- fields (required and not)
- properties (including get/set/init accessors, required and not)
- events (including add/remove accessors)
- Parameter modifiers (ref, out, in, params)
- Attributes (including generic attributes and security attributes)
Expand Down Expand Up @@ -110,6 +110,7 @@ This document provides guidance for thinking about language interactions and tes
- pre-processing directives
- COM interop
- modopt and modreq
- CompilerFeatureRequiredAttribute
- ref assemblies
- extern alias
- UnmanagedCallersOnly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -431,5 +431,39 @@ static internal class C2
static internal class Nested { }
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsOrderModifiers)]
public async Task RequiredAfterAllOnProp()
{
await TestInRegularAndScriptAsync("""
class C
{
[|required|] public virtual unsafe int Prop { get; init; }
}
""",
"""
class C
{
public virtual unsafe required int Prop { get; init; }
}
""");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsOrderModifiers)]
public async Task RequiredAfterAllButVolatileOnField()
{
await TestInRegularAndScriptAsync("""
class C
{
[|required|] public unsafe volatile int Field;
}
""",
"""
class C
{
public unsafe required volatile int Field;
}
""");
}
}
}
3 changes: 3 additions & 0 deletions src/CodeStyle/Core/Analyzers/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsOverride.get -> b
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsPartial.get -> bool
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsReadOnly.get -> bool
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsRef.get -> bool
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsRequired.get -> bool
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsSealed.get -> bool
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsStatic.get -> bool
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.IsUnsafe.get -> bool
Expand All @@ -56,6 +57,7 @@ Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsNew(bool isNe
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsOverride(bool isOverride) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsReadOnly(bool isReadOnly) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsRef(bool isRef) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsRequired(bool isRequired) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsSealed(bool isSealed) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsStatic(bool isStatic) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.WithIsUnsafe(bool isUnsafe) -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
Expand Down Expand Up @@ -84,6 +86,7 @@ static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Override.get
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Partial.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.ReadOnly.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Ref.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Required.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Sealed.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.Static.get -> Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers
static Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers.TryParse(string value, out Microsoft.CodeAnalysis.Internal.Editing.DeclarationModifiers modifiers) -> bool
Expand Down
8 changes: 7 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
diagnostics,
out var memberResolutionResult,
out var candidateConstructors,
allowProtectedConstructorsOfBaseType: true);
allowProtectedConstructorsOfBaseType: true,
suppressUnsupportedRequiredMembersError: false);
attributeConstructor = memberResolutionResult.Member;
expanded = memberResolutionResult.Resolution == MemberResolutionKind.ApplicableInExpandedForm;
argsToParamsOpt = memberResolutionResult.Result.ArgsToParamsOpt;
Expand Down Expand Up @@ -238,6 +239,11 @@ private BoundAttribute BindAttributeCore(AttributeSyntax node, NamedTypeSymbol a
ImmutableArray<BoundAssignmentOperator> boundNamedArguments = analyzedArguments.NamedArguments?.ToImmutableAndFree() ?? ImmutableArray<BoundAssignmentOperator>.Empty;
Debug.Assert(boundNamedArguments.All(arg => !arg.Right.NeedsToBeConverted()));

if (attributeConstructor is not null)
{
CheckRequiredMembersInObjectInitializer(attributeConstructor, ImmutableArray<BoundExpression>.CastUp(boundNamedArguments), node, diagnostics);
}

analyzedArguments.ConstructorArguments.Free();

return new BoundAttribute(
Expand Down
145 changes: 139 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4073,7 +4073,8 @@ private BoundExpression BindConstructorInitializerCore(
diagnostics,
out memberResolutionResult,
out candidateConstructors,
allowProtectedConstructorsOfBaseType: true);
allowProtectedConstructorsOfBaseType: true,
suppressUnsupportedRequiredMembersError: true);
MethodSymbol resultMember = memberResolutionResult.Member;

validateRecordCopyConstructor(constructor, baseType, resultMember, errorLocation, diagnostics);
Expand Down Expand Up @@ -4125,6 +4126,13 @@ private BoundExpression BindConstructorInitializerCore(
diagnostics);
}

if (resultMember.HasSetsRequiredMembers && !constructor.HasSetsRequiredMembers)
{
hasErrors = true;
// This constructor must add 'SetsRequiredMembers' because it chains to a constructor that has that attribute.
diagnostics.Add(ErrorCode.ERR_ChainingToSetsRequiredMembersRequiresSetsRequiredMembers, errorLocation);
}

return new BoundCall(
nonNullSyntax,
receiver,
Expand Down Expand Up @@ -4977,6 +4985,102 @@ private static void ReportDuplicateObjectMemberInitializers(BoundExpression boun
}
}

#nullable enable
private static void CheckRequiredMembersInObjectInitializer(
MethodSymbol constructor,
ImmutableArray<BoundExpression> initializers,
SyntaxNode creationSyntax,
BindingDiagnosticBag diagnostics)
{
if (!constructor.ShouldCheckRequiredMembers())
{
return;
}

if (constructor.ContainingType.HasRequiredMembersError)
{
// An error will be reported on the constructor if from source, or a use-site diagnostic will be reported on the use if from metadata.
return;
}

var requiredMembers = constructor.ContainingType.AllRequiredMembers;

if (requiredMembers.Count == 0)
{
return;
}

var requiredMembersBuilder = requiredMembers.ToBuilder();

if (initializers.IsDefaultOrEmpty)
{
reportMembers();
return;
}

foreach (var initializer in initializers)
{
if (initializer is not BoundAssignmentOperator assignmentOperator)
{
continue;
}

var memberSymbol = assignmentOperator.Left switch
{
// Regular initializers
BoundObjectInitializerMember member => member.MemberSymbol,
// Attribute initializers
BoundPropertyAccess propertyAccess => propertyAccess.PropertySymbol,
BoundFieldAccess fieldAccess => fieldAccess.FieldSymbol,
// Error cases
_ => null
};

if (memberSymbol is null)
{
continue;
}

if (!requiredMembersBuilder.TryGetValue(memberSymbol.Name, out var requiredMember))
{
continue;
}

if (!memberSymbol.Equals(requiredMember, TypeCompareKind.ConsiderEverything))
{
continue;
}

requiredMembersBuilder.Remove(memberSymbol.Name);

if (assignmentOperator.Right is BoundObjectInitializerExpressionBase initializerExpression)
{
// Required member '{0}' must be assigned a value, it cannot use a nested member or collection initializer.
diagnostics.Add(ErrorCode.ERR_RequiredMembersMustBeAssignedValue, initializerExpression.Syntax.Location, requiredMember);
}
}

reportMembers();

void reportMembers()
{
Location location = creationSyntax switch
{
ObjectCreationExpressionSyntax { Type: { } type } => type.Location,
BaseObjectCreationExpressionSyntax { NewKeyword: { } newKeyword } => newKeyword.GetLocation(),
AttributeSyntax { Name: { } name } => name.Location,
_ => creationSyntax.Location
};

foreach (var (_, member) in requiredMembersBuilder)
{
// Required member '{0}' must be set in the object initializer or attribute constructor.
diagnostics.Add(ErrorCode.ERR_RequiredMemberMustBeSet, location, member);
}
}
}
#nullable disable

private BoundCollectionInitializerExpression BindCollectionInitializerExpression(
InitializerExpressionSyntax initializerSyntax,
TypeSymbol initializerType,
Expand Down Expand Up @@ -5375,7 +5479,8 @@ protected BoundExpression BindClassCreationExpression(
diagnostics,
out MemberResolutionResult<MethodSymbol> memberResolutionResult,
out ImmutableArray<MethodSymbol> candidateConstructors,
allowProtectedConstructorsOfBaseType: false) &&
allowProtectedConstructorsOfBaseType: false,
suppressUnsupportedRequiredMembersError: false) &&
!type.IsAbstract)
{
var method = memberResolutionResult.Member;
Expand Down Expand Up @@ -5421,7 +5526,7 @@ protected BoundExpression BindClassCreationExpression(
}

boundInitializerOpt = makeBoundInitializerOpt();
result = new BoundObjectCreationExpression(
var creation = new BoundObjectCreationExpression(
node,
method,
candidateConstructors,
Expand All @@ -5437,7 +5542,9 @@ protected BoundExpression BindClassCreationExpression(
type,
hasError);

return result;
CheckRequiredMembersInObjectInitializer(creation.Constructor, creation.InitializerExpressionOpt?.Initializers ?? default, creation.Syntax, diagnostics);

return creation;
}

LookupResultKind resultKind;
Expand Down Expand Up @@ -5709,7 +5816,8 @@ internal bool TryPerformConstructorOverloadResolution(
BindingDiagnosticBag diagnostics,
out MemberResolutionResult<MethodSymbol> memberResolutionResult,
out ImmutableArray<MethodSymbol> candidateConstructors,
bool allowProtectedConstructorsOfBaseType) // Last to make named arguments more convenient.
bool allowProtectedConstructorsOfBaseType,
bool suppressUnsupportedRequiredMembersError) // Last to make named arguments more convenient.
{
// Get accessible constructors for performing overload resolution.
ImmutableArray<MethodSymbol> allInstanceConstructors;
Expand Down Expand Up @@ -5757,7 +5865,7 @@ internal bool TryPerformConstructorOverloadResolution(
}
}

diagnostics.Add(errorLocation, useSiteInfo);
ReportConstructorUseSiteDiagnostics(errorLocation, diagnostics, suppressUnsupportedRequiredMembersError, useSiteInfo);

if (succeededIgnoringAccessibility)
{
Expand Down Expand Up @@ -5814,6 +5922,31 @@ internal bool TryPerformConstructorOverloadResolution(
return succeededConsideringAccessibility;
}

internal static bool ReportConstructorUseSiteDiagnostics(Location errorLocation, BindingDiagnosticBag diagnostics, bool suppressUnsupportedRequiredMembersError, CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (suppressUnsupportedRequiredMembersError && useSiteInfo.AccumulatesDiagnostics && useSiteInfo.Diagnostics is { Count: not 0 })
{
diagnostics.AddDependencies(useSiteInfo);
foreach (var diagnostic in useSiteInfo.Diagnostics)
{
// We don't want to report this error here because we'll report ERR_RequiredMembersBaseTypeInvalid. That error is suppressable by the
// user using the `SetsRequiredMembers` attribute on the constructor, so reporting this error would prevent that from working.
if ((ErrorCode)diagnostic.Code == ErrorCode.ERR_RequiredMembersInvalid)
{
continue;
}

diagnostics.ReportUseSiteDiagnostic(diagnostic, errorLocation);
}

return true;
}
else
{
return diagnostics.Add(errorLocation, useSiteInfo);
}
}

private ImmutableArray<MethodSymbol> GetAccessibleConstructorsForOverloadResolution(NamedTypeSymbol type, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
ImmutableArray<MethodSymbol> allInstanceConstructors;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ internal struct ProcessedFieldInitializers
{
internal ImmutableArray<BoundInitializer> BoundInitializers { get; set; }
internal BoundStatement? LoweredInitializers { get; set; }
internal NullableWalker.VariableState AfterInitializersState;
internal bool HasErrors { get; set; }
internal ImportChain? FirstImportChain { get; set; }
}
Expand Down
Loading