Skip to content
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

Use ParamCollectionAttribute to mark params collections in metadata #71747

Merged

Conversation

AlekseyTs
Copy link
Contributor

Corresponding speclet update is dotnet/csharplang#7850

@AlekseyTs AlekseyTs requested review from a team as code owners January 22, 2024 05:36
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Jan 22, 2024
@AlekseyTs AlekseyTs marked this pull request as draft January 22, 2024 15:51
@AlekseyTs AlekseyTs marked this pull request as ready for review January 22, 2024 16:41
@AlekseyTs
Copy link
Contributor Author

@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review

@RikkiGibson RikkiGibson self-assigned this Jan 22, 2024
@AlekseyTs
Copy link
Contributor Author

@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review

@@ -124,6 +124,7 @@ private void EnsureAttributesExist(TypeCompilationState compilationState)
}

ParameterHelpers.EnsureRefKindAttributesExist(moduleBuilder, Parameters);
// Not emitting ParamCollectionAttribute/ParamArrayAttribute for these methods it is not a SynthesizedDelegateInvokeMethod
Copy link
Contributor

@RikkiGibson RikkiGibson Jan 22, 2024

Choose a reason for hiding this comment

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

Suggested change
// Not emitting ParamCollectionAttribute/ParamArrayAttribute for these methods it is not a SynthesizedDelegateInvokeMethod
// Not emitting ParamCollectionAttribute/ParamArrayAttribute for these methods because it is not a SynthesizedDelegateInvokeMethod
``` #Closed

@AlekseyTs
Copy link
Contributor Author

@333fred, @dotnet/roslyn-compiler For the second review

1 similar comment
@AlekseyTs
Copy link
Contributor Author

@333fred, @dotnet/roslyn-compiler For the second review

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

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

Done review pass

@@ -13,6 +13,8 @@ internal partial class LocalRewriter
{
public override BoundNode VisitDelegateCreationExpression(BoundDelegateCreationExpression node)
{
Debug.Assert(!node.Type.IsAnonymousType); // Missing EnsureParamCollectionAttributeExists call?
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

Is this supposed to be a prototype comment? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this supposed to be a prototype comment?
No

result |= IsParamsValues.Collection;
}

_lazyIsParams = result;
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

The normal pattern we use for these is using ThreadSafeFlagsOperation.Set. I would prefer if we did that instead. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The normal pattern we use for these is using ThreadSafeFlagsOperation.Set. I would prefer if we did that instead.

I do not think it is warranted in this case, we are not changing different bits concurrently. The value is initialized once. It used to be ThreeState, now it is four state. Once initialized, no bits are changing.

Collection = 4,
}

private IsParamsValues _lazyIsParams;
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

If we're going to add a new flags (it looks like PackedFlags is at its size limit), should we consider just adding a second PackedFlags int now, and putting these bits in there? It should have the same effect on type size, and is more consistent with the standard way we do this here. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I do not intend to merge this value into PackedFlags and the change in the field's type doesn't increase memory used. It used to be byte backed enum and it remains byte backed enum, only the set of possible values changed. It used to be a "monolithic" value and it remains a "monolithic" value. The bit nature of the enum is simply to make it easier to work with the value, it is not like parts of the value are changing concurrently with other parts of the value. I could easily enumerate all possible combinations , encode them as regular values and work with them as such. However the code would be more complicated then.

public sealed override bool IsParams => (_parameterSyntaxKind & ParameterSyntaxKind.ParamsParameter) != 0;
public sealed override bool IsParamArray => (_parameterSyntaxKind & ParameterSyntaxKind.ParamsParameter) != 0 && this.parameterType.IsSZArray();

public sealed override bool IsParamCollection => (_parameterSyntaxKind & ParameterSyntaxKind.ParamsParameter) != 0 && !this.parameterType.IsSZArray();
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

Do we need to be more precise for this and actually check that the parameter type is a valid params collection type? We validate that for IsParamArray, and I'm a bit worried that the symmetry with naming here implies that if IsParamsCollection is true, the parameter is a valid params collection. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do we need to be more precise for this and actually check that the parameter type is a valid params collection type?

No we do not need that, and that would actually be incorrect. If there is a params modifier in code, one of the properties should return true. The property is not meant to indicate validity of the modifier, just its presence. Therefore, what is not a valid params array is considered a params collection. It used to be that original IsParams property was true even for non-array types.

@@ -263,6 +264,14 @@ private static void EnsureRefKindAttributesExist(CSharpCompilation compilation,
}
}

internal static void EnsureParamCollectionAttributeExistsAndModifyCompilation(CSharpCompilation compilation, ImmutableArray<ParameterSymbol> parameters, BindingDiagnosticBag diagnostics)
{
if (parameters.FirstOrDefault(static (p) => p.IsParamCollection) is { } parameter)
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

Minor nit: it would probably be more efficient to use LastOrDefault so we start the search from the last parameter, rather than the first, as that will be the right parameter in 99% of cases, and I don't think it matters if we report errors on the second params parameter instead of the first if a user specified multiple in error. #Resolved

}
";

private static void VerifyParamAttribute(ParameterSymbol parameter, bool isParamArray = false, bool isParamCollection = false)
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

Small nit: would be slightly clearer for tests if this was named VerifyParamsAndAttribute, so that when I reading the validators I understand that this verifies both the standard params properties and attribute presence. #Resolved

}
}
""";
CreateCompilationWithIL(src, il).VerifyDiagnostics();
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

Nit: VerifyEmitDiagnostics #Resolved

}
}
""";
CreateCompilation(src).VerifyDiagnostics();
Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

Nit: VerifyEmitDiagnostics #Resolved

Comment on lines 6209 to 6222
comp = CreateCompilationWithIL("", il);

test1 = comp.GetMember<MethodSymbol>("Params.Test1").Parameters.Last();
test2 = comp.GetMember<MethodSymbol>("Params.Test2").Parameters.Last();

Assert.Empty(test1.GetAttributes());
Assert.Empty(test2.GetAttributes());

VerifyParamAttribute(test1, isParamCollection: true);
VerifyParamAttribute(test2, isParamArray: true);

AssertEx.Equal("System.Runtime.CompilerServices.ParamCollectionAttribute", test1.GetCustomAttributesToEmit(null).Single().ToString());
AssertEx.Equal("System.ParamArrayAttribute", test2.GetCustomAttributesToEmit(null).Single().ToString());

Copy link
Member

@333fred 333fred Jan 25, 2024

Choose a reason for hiding this comment

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

I'm not sure if I'm missing something, but this seems to be exactly the same as the first part of the test, minus 2 calls to VerifyParamAttribute on test1 and test2? #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure if I'm missing something, but this seems to be exactly the same as the first part of the test, minus 2 calls to VerifyParamAttribute on test1 and test2?

Are you referring to the to different sets of asserts performed on "equal" compilations above?

Since PEParameterSymbol._lazyIsParams can be initialized by two different APIs, the IsParamArray/IsParamCollection properties and GetAttributes, I am asserting that result doesn't depend on the order in which the APIs are used first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Feature - ParamsCollections untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants