-
Notifications
You must be signed in to change notification settings - Fork 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
Permit non-void return types for partial methods (VS 16.8, .NET 5) #3301
Comments
I presume that unlike void private methods, an implementation of the partial method would be required? |
@YairHalberstadt I personally have no objections to that, but it would be up to the language teams to make the final decision. I would imagine some complexity if that wasn't the case though. |
I independently thought of the same thing when @chsienki and I were brainstorming a while ago. Seems useful. I assumed that non-void return types would require an implementation method. |
Yes indeed. It would also mean that
I think we'd also want to lift that restriction. Think the best way to approach that part is to say that |
If Would it be worth something allowing the user to specify that the |
(To clarify, I mean a specific instance is required so we don't break back-compat). |
|
Yes. As my comment said: partial methods which have a non-private accessibility are required to have an implementation. I deliberately left out return type in that statement.
Possibly but I'd wait to see a compelling case before adding a new feature to support this. So far we've been discussing relaxing existing restrictions to support source generators. This would be adding a new restriction and is essentially a new feature (need syntax or attribute to support). |
Previous discussion: #753 |
Sure, just wanted to add the link just in case. Closed. |
I don't actually understand the need here for this. Addressing hte OP:
The generated Foo includes its documentation. That documentation flows along the same way as nodemal code.
IDE works because it sees the generated code.
That should not happen. The source generator will run and will have generated the method the user can call. a |
The auto-generated code isn't what the user wrote, so how would it know the documentation for the API? In the below code, where does the XML documentation for the function
That is only true if source generators are continually running in the background. It is entirely possible for this to work similar to XAML, but there are terrible cases there especially during a copy/paste of large swathes of XAML where the editor needs to wait for the language service to keep up. There would be squiggles and general confusion since In the proposed case, there is a function declaration a head of time which satisfies the location for XML documentation and completely addresses the IntelliSense issue without putting undo pressure on the IDE to try and generate potentially large amounts of code during the edit cycle. |
Flows from where? Let's say this is a regex generator. I want to be able to write this: /// <summary>Creates a regex that finds hello followed by world, with anything in the middle</summary>
[RegexGenerator("hello.*world", RegexOptions.IgnoreCase)]
internal static partial Regex HelloWorldRegex(); With that I don't have to have a string-based name. I've written the method I'll be calling like any other, except the generator is filling in the implementation, rather than the method magically appearing out of thing air. I've been able to put documentation on the method like I would on any other method. The regular expression that's tied to this method is right there in the method's attribute, not somewhere else in the assembly attributing some less specific thing. Etc. How does this look without the non-void returning support? |
Yes. That's the plan here. |
I would not write it like that. i would just have: [RegexGenerator("hello.*world", RegexOptions.IgnoreCase, "HelloWorldRegex")]
partial class Whatever
{
} SourceGenerator would take that and make another part for |
Yeah, to me personally that's much worse, for all the reasons I outlined. And, to the documentation point, it lacks it. |
I'm not really understanding what you mean. When the generator generates the new code it can put whatever comments on it using whatever scheme you want to use to convey information to your generator. It could be embedded in attributes, or comments, or something else. |
This sounds like the |
you don't need a string based name, the following works fine:
there's a duality here. teh generator can see the above and use it to make the method called |
|
Why would I want that when we already have a long-standing, compiler-supported mechanism for this? I don't want to invent my own mechanisms, worry that an arbitrary comment in just the wrong place is going to be misconstrued by the ad-hoc conventions of the generator. |
That would be weird. We're not going to run diagnostics on hte code prior to source generation. Expectation is we run it after source generation. IN other words, it's part of our design that the code prior to gen is normally expected to be in error because it will depend on the generated code. In other words, the thing you're trying to fix/avoid here is precisely the use model we're trying heavily to move toward. the expectation is absolutely to not have the definitions or aything else informing the original source code. but all IDE features and whatnot only operate on the post-transformed code. |
Because it would work? And it requires no language changes at all.
But you're asking us to invent a mechanism. Right now the benefit of the invention we have is that it is entirely divorced from the language. It is simply: you provide information however you want, and you generate however you want. We only assume that the generated state of the world is valid and worth doing anything with. We don't need any sort of language handshakes to invent. It's all just an api. Just like analyzers don't require anything special and can rev entirely separately from the lang, we'd like the same for generators. |
Then how is the generated code ever used? A key invariant of the source generators is that they can't modify any user defined code. How is this new code called/consumed without use of reflection? |
I'm asking the language to add a small feature that makes the source generator way more usable and user-friendly, in my opinion.
And the feedback I'm sharing, and that others here are sharing, and offline conversations from even more folks sharing this, is that this is very suboptimal. |
This isn't a requirement for sure. This simply makes the Source Generator feature usable in more scenarios (e.g. AOT). |
// this is normal user source:
void Foo()
{
var v = Whatever.HellowWorldRegex;
}
[Regex(...)] // see above for example
partial class Whatever
{
} This just compiles. It works because the generator sees whatever it needs and generates: partial class Whatever
{
public Regex HelloWorldRegex => ...
} It is very intentionally by design that the original source on its own with any compiler would not compile. And it can only compile and have meaning with the generator doing its thing. |
Can you explain how it's more user friendly? For users consuming the generator it's going to be the same right? They'll simply see the final state of things and all things like intellisense, docs and whatnot will be the same. In terms of people creating the generator, i'm happy to work on all sorts of IDE features to make that a pleasant experience. but i'm not seeing the need for a language change. |
Fair enough. Seemed oogy to have an entirely source-oriented solution make it's way into metadata. But if it doesn't offend people, it's not going to bother me that much :) |
To reiterate my point on dotnet/roslyn#40162 (comment), one of the commonly requested use cases for source generators is generating calls to NotifyPropertyChanged from an auto property, without having to create a full property, via usage of an attribute. This is tricky with the current proposal, but partial properties would make this trivial to do. |
You might not even need it to be a well known type. Conditional attribute should work: using System;
using System.Diagnostics;
[Conditional("sgadfbvsedbbs")]
public class SourceGeneratorAttribute : Attribute {
}
public class MyAttribute : SourceGeneratorAttribute
{
[MyAttribute]
public void M() {}
} The usage of MyAttribute is not emitted into metadata: https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxASwDYB8ACAmARgFgAoHABgAIcCA6AETQgHMA7Ae1jQGMoBuUqQDaAYXasAJmhhpxEDAAoARFGYQJAM2AA3KHAnBgUJQEoAuqRwBmankoBldgFcE3OAHE4rRBBjsEAIIwMAhowE7wlCCUQSFhEXCUAN6CJAC+qda2lACyAJ6xoeGR0Y4ubp7eCL7+hfGRpCkklC0tQvl1xXAWza3UNjgALLkKJskZ6UA== (With thanks to @HaloFour) |
Thinking out loud here, this could be actually used where you need replace/original, using some lightweight code convention that is completely in the control of the aurhor, take cached method example: [Cached]
public partial int Compute(int i);
private int ComputeImpl(int i) {} The generator will look for an That said, I support |
Right now the 'partial' modifier is required to be the last modifier before the type. As part of this feature should we relax this requirement? |
That's an interesting use case. I could see the (ab)use of partial properties as stubs for a generator to fill in a specific aspect: [IPNC]
public partial class Foo {
public partial string Name { get; set; }
}
// generated:
public partial class Foo : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public partial string Name {
get => _name;
set {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(Name)));
}
}
} The obvious problem with it is that it's not composable, you'd only ever be able to apply a single aspect. I honestly hope that the team will reconsider |
I've done a standalone implementation of that long time ago and it's already being considered as a pre-requirement for records (dotnet/roslyn#42097) |
Since the final method is being generated at last, you can build the composability into your generator with some code tricks, no? Also, replace/original would not solve that because all generators see the original compilation. That's a non-issue for IIRC using multiple |
I could see that if you were composing multiple aspects supported by the same generator, but only if the generator were crafted to interpret those multiple aspects and emit a single implementation taking all of those aspects into consideration. Other than that I don't see composability being practical.
Isn't it still an open question? What happens if you reference multiple generators now and two of them want to emit the same types or members? Can each generator see what the other generator has already emitted? Do they run in a deterministic order and at deterministic times? |
I don't think so. The issue seems to be resolved because we don't depend on it anymore.
I wouldn't expect anything other than a compile-time error.
No, and the order is irrelevant as each generator can only see the original compilation from source. |
Sounds like that would make it practically impossible to compose multiple aspects backed by different generators. |
One possible solution is to have a plugin-based framework on top of generators API to be able to chain multiple generator "plugins" with a defined execution order. Though I think that would be only widely usable if it's from MS. |
Execution order and convention for emitting multiple partial methods into which each generator can put its implementation and chain to the next. Doable, but I don't think it's practical without some major backer. But maybe necessity will drive it. |
I'm not sure why this issue is still open. C# 9 already supports partial methods with non-void return types (you just need to explicitly specify a visibility), e.g. |
It's already implemented and working.
For non-void returing partial methods, you must include an accessibility modifier.
Feature issues stay open until they're documented in the ECMA spec. See also the tag |
I feel like that's not a good use case at all. If there is a convention, you could just declare |
The Source Generator feature is being investigated to create a runtime agnostic and AOT compatible P/Invoke stub generator. One of the hard requirements of Source Generators is user written code is read only and cannot be altered by any Source Generator implementation. This requirement makes it difficult to declare methods and use them, but have a Source Generator provide an implementation in the future (i.e. build time).
Possible work-arounds exist by declaring a partial class and having a convention.
The above could be made to work, but there are issues.
Foo
work?An alternative approach would be a type of forward declaration using
partial
methods. The below would relax the requirement to have avoid
return type and thus the generation of the corresponding method could occur. IntelliSense would make sense as would a location for XML documentation in the user defined code.Note
partial
methods are also required to beprivate
. The below example does adhere to that requirement, but relaxing that would also be beneficial although not strictly needed./cc @jaredpar @davidwrighton @jkotas @stephentoub
The text was updated successfully, but these errors were encountered: