Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public QueryUnboundLambdaState(Binder binder, RangeVariableMap rangeVariableMap,

public override string ParameterName(int index) { return _parameters[index].Name; }
public override bool ParameterIsDiscard(int index) { return false; }
public override bool ParameterIsNullChecked(int index) { return false; }
public override SyntaxList<AttributeListSyntax> ParameterAttributes(int index) => default;
public override bool HasNames { get { return true; } }
public override bool HasSignature { get { return true; } }
Expand Down
18 changes: 17 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Lambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

ImmutableArray<string> names = default;
ImmutableArray<RefKind> refKinds = default;
ImmutableArray<bool> nullCheckedOpt = default;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a BitVector for this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable. Will look at this in a follow-up PR.

ImmutableArray<TypeWithAnnotations> types = default;
RefKind returnRefKind = RefKind.None;
TypeWithAnnotations returnType = default;
Expand All @@ -63,6 +64,10 @@ private UnboundLambda AnalyzeAnonymousFunction(
hasSignature = true;
var simple = (SimpleLambdaExpressionSyntax)syntax;
namesBuilder.Add(simple.Parameter.Identifier.ValueText);
if (isNullChecked(simple.Parameter))
{
nullCheckedOpt = ImmutableArray.Create(true);
}
break;
case SyntaxKind.ParenthesizedLambdaExpression:
// (T x, U y) => ...
Expand Down Expand Up @@ -98,6 +103,7 @@ private UnboundLambda AnalyzeAnonymousFunction(

var typesBuilder = ArrayBuilder<TypeWithAnnotations>.GetInstance();
var refKindsBuilder = ArrayBuilder<RefKind>.GetInstance();
var nullCheckedBuilder = ArrayBuilder<bool>.GetInstance();
var attributesBuilder = ArrayBuilder<SyntaxList<AttributeListSyntax>>.GetInstance();

// In the batch compiler case we probably should have given a syntax error if the
Expand Down Expand Up @@ -176,6 +182,7 @@ private UnboundLambda AnalyzeAnonymousFunction(
namesBuilder.Add(p.Identifier.ValueText);
typesBuilder.Add(type);
refKindsBuilder.Add(refKind);
nullCheckedBuilder.Add(isNullChecked(p));
attributesBuilder.Add(syntax.Kind() == SyntaxKind.ParenthesizedLambdaExpression ? p.AttributeLists : default);
}

Expand All @@ -191,13 +198,19 @@ private UnboundLambda AnalyzeAnonymousFunction(
refKinds = refKindsBuilder.ToImmutable();
}

if (nullCheckedBuilder.Contains(true))
{
nullCheckedOpt = nullCheckedBuilder.ToImmutable();
}

if (attributesBuilder.Any(a => a.Count > 0))
{
parameterAttributes = attributesBuilder.ToImmutable();
}

typesBuilder.Free();
refKindsBuilder.Free();
nullCheckedBuilder.Free();
attributesBuilder.Free();
}

Expand All @@ -208,7 +221,10 @@ private UnboundLambda AnalyzeAnonymousFunction(

namesBuilder.Free();

return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, types, names, discardsOpt, isAsync, isStatic);
return UnboundLambda.Create(syntax, this, diagnostics.AccumulatesDependencies, returnRefKind, returnType, parameterAttributes, refKinds, types, names, discardsOpt, nullCheckedOpt, isAsync, isStatic);

static bool isNullChecked(ParameterSyntax parameter)
=> parameter.ExclamationExclamationToken.IsKind(SyntaxKind.ExclamationExclamationToken);

static ImmutableArray<bool> computeDiscards(SeparatedSyntaxList<ParameterSyntax> parameters, int underscoresCount)
{
Expand Down
15 changes: 13 additions & 2 deletions src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ public static UnboundLambda Create(
ImmutableArray<TypeWithAnnotations> types,
ImmutableArray<string> names,
ImmutableArray<bool> discardsOpt,
ImmutableArray<bool> nullCheckedOpt,
bool isAsync,
bool isStatic)
{
Expand All @@ -371,7 +372,7 @@ public static UnboundLambda Create(
bool hasErrors = !types.IsDefault && types.Any(t => t.Type?.Kind == SymbolKind.ErrorType);

var functionType = FunctionTypeSymbol.Lazy.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => ((UnboundLambda)expr).Data.InferDelegateType());
var data = new PlainUnboundLambdaState(binder, returnRefKind, returnType, parameterAttributes, names, discardsOpt, types, refKinds, isAsync, isStatic, includeCache: true);
var data = new PlainUnboundLambdaState(binder, returnRefKind, returnType, parameterAttributes, names, discardsOpt, nullCheckedOpt, types, refKinds, isAsync, isStatic, includeCache: true);
var lambda = new UnboundLambda(syntax, data, functionType, withDependencies, hasErrors: hasErrors);
data.SetUnboundLambda(lambda);
functionType?.SetExpression(lambda.WithNoCache());
Expand Down Expand Up @@ -438,6 +439,7 @@ public TypeWithAnnotations InferReturnType(ConversionsBase conversions, NamedTyp
public Location ParameterLocation(int index) { return Data.ParameterLocation(index); }
public string ParameterName(int index) { return Data.ParameterName(index); }
public bool ParameterIsDiscard(int index) { return Data.ParameterIsDiscard(index); }
public bool ParameterIsNullChecked(int index) { return Data.ParameterIsNullChecked(index); }
}

internal abstract class UnboundLambdaState
Expand Down Expand Up @@ -497,6 +499,7 @@ internal UnboundLambdaState WithCaching(bool includeCache)
public abstract MessageID MessageID { get; }
public abstract string ParameterName(int index);
public abstract bool ParameterIsDiscard(int index);
public abstract bool ParameterIsNullChecked(int index);
public abstract SyntaxList<AttributeListSyntax> ParameterAttributes(int index);
public abstract bool HasSignature { get; }
public abstract bool HasExplicitReturnType(out RefKind refKind, out TypeWithAnnotations returnType);
Expand Down Expand Up @@ -1285,6 +1288,7 @@ internal sealed class PlainUnboundLambdaState : UnboundLambdaState
private readonly ImmutableArray<SyntaxList<AttributeListSyntax>> _parameterAttributes;
private readonly ImmutableArray<string> _parameterNames;
private readonly ImmutableArray<bool> _parameterIsDiscardOpt;
private readonly ImmutableArray<bool> _parameterIsNullCheckedOpt;
private readonly ImmutableArray<TypeWithAnnotations> _parameterTypesWithAnnotations;
private readonly ImmutableArray<RefKind> _parameterRefKinds;
private readonly bool _isAsync;
Expand All @@ -1297,6 +1301,7 @@ internal PlainUnboundLambdaState(
ImmutableArray<SyntaxList<AttributeListSyntax>> parameterAttributes,
ImmutableArray<string> parameterNames,
ImmutableArray<bool> parameterIsDiscardOpt,
ImmutableArray<bool> parameterIsNullCheckedOpt,
ImmutableArray<TypeWithAnnotations> parameterTypesWithAnnotations,
ImmutableArray<RefKind> parameterRefKinds,
bool isAsync,
Expand All @@ -1309,6 +1314,7 @@ internal PlainUnboundLambdaState(
_parameterAttributes = parameterAttributes;
_parameterNames = parameterNames;
_parameterIsDiscardOpt = parameterIsDiscardOpt;
_parameterIsNullCheckedOpt = parameterIsNullCheckedOpt;
_parameterTypesWithAnnotations = parameterTypesWithAnnotations;
_parameterRefKinds = parameterRefKinds;
_isAsync = isAsync;
Expand Down Expand Up @@ -1378,6 +1384,11 @@ public override bool ParameterIsDiscard(int index)
return _parameterIsDiscardOpt.IsDefault ? false : _parameterIsDiscardOpt[index];
}

public override bool ParameterIsNullChecked(int index)
{
return _parameterIsNullCheckedOpt.IsDefault ? false : _parameterIsNullCheckedOpt[index];
}

public override RefKind RefKind(int index)
{
Debug.Assert(0 <= index && index < _parameterTypesWithAnnotations.Length);
Expand All @@ -1393,7 +1404,7 @@ public override TypeWithAnnotations ParameterTypeWithAnnotations(int index)

protected override UnboundLambdaState WithCachingCore(bool includeCache)
{
return new PlainUnboundLambdaState(Binder, _returnRefKind, _returnType, _parameterAttributes, _parameterNames, _parameterIsDiscardOpt, _parameterTypesWithAnnotations, _parameterRefKinds, _isAsync, _isStatic, includeCache);
return new PlainUnboundLambdaState(Binder, _returnRefKind, _returnType, _parameterAttributes, _parameterNames, _parameterIsDiscardOpt, _parameterIsNullCheckedOpt, _parameterTypesWithAnnotations, _parameterRefKinds, _isAsync, _isStatic, includeCache);
}

protected override BoundExpression? GetLambdaExpressionBody(BoundBlock body)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1799,7 +1799,7 @@ private MethodSymbol UnsafeGetNullableMethod(SyntaxNode syntax, TypeSymbol nulla
/// Recommendation: Do not use, use <see cref="TryGetNullableMethod"/> instead!
/// If used, a unit-test with a missing member is absolutely a must have.
/// </summary>
private static MethodSymbol UnsafeGetNullableMethod(SyntaxNode syntax, TypeSymbol nullableType, SpecialMember member, CSharpCompilation compilation, BindingDiagnosticBag diagnostics)
internal static MethodSymbol UnsafeGetNullableMethod(SyntaxNode syntax, TypeSymbol nullableType, SpecialMember member, CSharpCompilation compilation, BindingDiagnosticBag diagnostics)
{
var nullableType2 = nullableType as NamedTypeSymbol;
Debug.Assert(nullableType2 is { });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed partial class LocalRewriter
{
private BoundBlock RewriteNullChecking(BoundBlock block)
private BoundBlock? RewriteNullChecking(BoundBlock? block)
{
if (block is null)
{
return null;
}

Debug.Assert(_factory.CurrentFunction is not null);
var statementList = TryConstructNullCheckedStatementList(_factory.CurrentFunction.Parameters, block.Statements, _factory);
if (statementList.IsDefault)
{
Expand All @@ -30,7 +31,7 @@ internal static ImmutableArray<BoundStatement> TryConstructNullCheckedStatementL
ImmutableArray<BoundStatement> existingStatements,
SyntheticBoundNodeFactory factory)
{
ArrayBuilder<BoundStatement> statementList = null;
ArrayBuilder<BoundStatement>? statementList = null;
foreach (ParameterSymbol param in parameters)
{
if (param.IsNullChecked)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1583,6 +1583,7 @@ internal BoundExpression MakeNullCheck(SyntaxNode syntax, BoundExpression rewrit

internal BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression expression)
{
// PROTOTYPE(param-nullchecking): consider restoring the 'private' accessibility of 'static LocalRewriter.UnsafeGetNullableMethod()'
return BoundCall.Synthesized(syntax, expression, LocalRewriter.UnsafeGetNullableMethod(syntax, expression.Type, CodeAnalysis.SpecialMember.System_Nullable_T_get_HasValue, Compilation, Diagnostics));
}

Expand Down Expand Up @@ -1653,7 +1654,7 @@ internal BoundExpression RewriteNullableNullEquality(
BoundExpression call = MakeNullableHasValue(syntax, nullable);
BoundExpression result = kind == BinaryOperatorKind.NullableNullNotEqual ?
call :
new BoundUnaryOperator(syntax, UnaryOperatorKind.BoolLogicalNegation, call, ConstantValue.NotAvailable, null, LookupResultKind.Viable, returnType);
new BoundUnaryOperator(syntax, UnaryOperatorKind.BoolLogicalNegation, call, ConstantValue.NotAvailable, methodOpt: null, constrainedToTypeOpt: null, LookupResultKind.Viable, returnType);

return result;
}
Expand Down
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/Parser/LanguageParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11499,8 +11499,8 @@ private bool ScanParenthesizedImplicitlyTypedLambda(Precedence precedence)
}

// Must have: ) =>
if (this.PeekToken(skipIndex + 1).Kind == SyntaxKind.CloseParenToken
&& this.PeekToken(skipIndex + 2).Kind == SyntaxKind.EqualsGreaterThanToken)
if (this.PeekToken(skipIndex).Kind == SyntaxKind.CloseParenToken
&& this.PeekToken(skipIndex + 1).Kind == SyntaxKind.EqualsGreaterThanToken)
{
return true;
}
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithCommaToken(
Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithLine(Microsoft.CodeAnalysis.SyntaxToken line) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax.WithOpenParenToken(Microsoft.CodeAnalysis.SyntaxToken openParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectivePositionSyntax
*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax.ExclamationExclamationToken.get -> Microsoft.CodeAnalysis.SyntaxToken
Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax.Update(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.SyntaxToken exclamationExclamationToken, Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax default) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax.WithExclamationExclamationToken(Microsoft.CodeAnalysis.SyntaxToken exclamationExclamationToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax
Microsoft.CodeAnalysis.CSharp.SyntaxKind.ExclamationExclamationToken = 8285 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
override Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.File.get -> Microsoft.CodeAnalysis.SyntaxToken
*REMOVED*Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.LineKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
override Microsoft.CodeAnalysis.CSharp.Syntax.LineDirectiveTriviaSyntax.LineKeyword.get -> Microsoft.CodeAnalysis.SyntaxToken
Expand Down Expand Up @@ -112,6 +116,7 @@ static Microsoft.CodeAnalysis.CSharp.CSharpExtensions.GetDeclaredSymbol(this Mic
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax> externs, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax> usings, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax> members) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.FileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.SyntaxToken namespaceKeyword, Microsoft.CodeAnalysis.CSharp.Syntax.NameSyntax name, Microsoft.CodeAnalysis.SyntaxToken semicolonToken, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.ExternAliasDirectiveSyntax> externs, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.UsingDirectiveSyntax> usings, Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax> members) -> Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.Parameter(Microsoft.CodeAnalysis.SyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.AttributeListSyntax> attributeLists, Microsoft.CodeAnalysis.SyntaxTokenList modifiers, Microsoft.CodeAnalysis.CSharp.Syntax.TypeSyntax type, Microsoft.CodeAnalysis.SyntaxToken identifier, Microsoft.CodeAnalysis.SyntaxToken exclamationExclamationToken, Microsoft.CodeAnalysis.CSharp.Syntax.EqualsValueClauseSyntax default) -> Microsoft.CodeAnalysis.CSharp.Syntax.ParameterSyntax
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor.VisitFileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax node) -> void
virtual Microsoft.CodeAnalysis.CSharp.CSharpSyntaxVisitor<TResult>.VisitFileScopedNamespaceDeclaration(Microsoft.CodeAnalysis.CSharp.Syntax.FileScopedNamespaceDeclarationSyntax node) -> TResult
Microsoft.CodeAnalysis.CSharp.SyntaxKind.RecordStructDeclaration = 9068 -> Microsoft.CodeAnalysis.CSharp.SyntaxKind
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ public LambdaParameterSymbol(
RefKind refKind,
string name,
bool isDiscard,
bool isNullChecked,
ImmutableArray<Location> locations)
: base(owner, ordinal, parameterType, refKind, name, locations, syntaxRef: null, isParams: false, isExtensionMethodThis: false)
{
_attributeLists = attributeLists;
IsDiscard = isDiscard;
IsNullChecked = isNullChecked;
}

public override bool IsDiscard { get; }

public override bool IsNullChecked { get; }

internal override bool IsMetadataOptional
{
get { return false; }
Expand Down
5 changes: 2 additions & 3 deletions src/Compilers/CSharp/Portable/Symbols/Source/LambdaSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ internal void GetDeclarationDiagnostics(BindingDiagnosticBag addTo)
foreach (var parameter in _parameters)
{
parameter.ForceComplete(locationOpt: null, cancellationToken: default);
ParameterHelpers.ReportParameterNullCheckingErrors(addTo.DiagnosticBag, parameter);
}

GetAttributes();
Expand Down Expand Up @@ -354,9 +355,7 @@ private ImmutableArray<ParameterSymbol> MakeParameters(
var location = unboundLambda.ParameterLocation(p);
var locations = location == null ? ImmutableArray<Location>.Empty : ImmutableArray.Create<Location>(location);

var parameter = new LambdaParameterSymbol(owner: this, attributeLists, type, ordinal: p, refKind, name, unboundLambda.ParameterIsDiscard(p), locations);
ParameterHelpers.AddNullCheckingErrorsToParameter(diagnostics, parameter);

var parameter = new LambdaParameterSymbol(owner: this, attributeLists, type, ordinal: p, refKind, name, unboundLambda.ParameterIsDiscard(p), unboundLambda.ParameterIsNullChecked(p), locations);
builder.Add(parameter);
}

Expand Down
Loading