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

Add Deprecated and Unimplemented attributes #785

Closed
wants to merge 9 commits into from
Closed

Conversation

swernli
Copy link
Collaborator

@swernli swernli commented Oct 12, 2023

This change adds support for two new attributes:

  • Deprecated which will produce a warning if the attributed callable is used and can accept either unit/() or a callable expression as the alternative. If an expression is provided, it will be name resolved and type checked.
  • Unimplemented which produces an error if the attributed callalbe is used and only accepts unit/().

The idea is to be able to use these to indicate parts of the standard library are either getting ready to be removed or entirely not present. The expectationsi that they could be used like this (using two old Bitwise utilities):

@Deprecated((a, b) -> a ^^^ b)
function Xor(a : Int, b : Int) : Int {
    return a ^^^ b;
}

@Unimplemented()
function XBits(paulis : Pauli[]) : Int {
    fail "unimplemented";
}

And here is how that would show up if user code called these functions:
image

@swernli swernli force-pushed the swernli/impl-attrs branch from 092a967 to 51c4514 Compare October 17, 2023 22:45
@swernli swernli marked this pull request as ready for review October 18, 2023 04:26
Copy link
Member

@minestarks minestarks left a comment

Choose a reason for hiding this comment

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

Left some comments. Code looks good but I have reservations about the design and the feature in general.

Comment on lines +1162 to +1165
/// Indicates that an item has been deprecated and may eventually be removed.
Deprecated(Option<Span>),
/// Indicates that an item does not have an implementation available for use.
Unimplemented,
Copy link
Member

Choose a reason for hiding this comment

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

I'm not really sold on having two separate attributes. Is Unimplemented like a more severe Deprecated? Or is it for things that we literally can't implement, where the implementation would just have a fail statement or something? In either case, I feel like we could just reuse the Deprecated attribute, perhaps with an additional flag or comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Deprecated is typically something implemented but doomed to be removed soon.

/// Indicates that a callable is an entry point to a program.
EntryPoint,
/// Indicates that an item has been deprecated and may eventually be removed.
Deprecated(Option<Span>),
Copy link
Member

Choose a reason for hiding this comment

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

I think it would be nice if this could take a user provided message (which would then be displayed in the warning).

@@ -1126,8 +1155,28 @@ impl Display for Ident {
/// An attribute.
#[derive(Clone, Debug, PartialEq)]
pub enum Attr {
/// Provide pre-processing information about when an item should be included in compilation.
Config,
Copy link
Member

Choose a reason for hiding this comment

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

Just a thought, not sure if I love it or not: Could we overload the @Config attribute for the purposes of deprecation? Like put some items behind a configuration that will never exist in practice (like None or literally Deprecated). The error message could then provide a little more context based on the configuration, or the attribute could take a user-provided message.

@@ -175,6 +176,16 @@ impl Checker {
));
}

fn check_attr_expr(&mut self, names: &Names, expr: &Expr) {
Copy link
Member

Choose a reason for hiding this comment

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

Honestly it feels weird to allow an expression here. Can we just allow a Path to a global if the intention is "use this alternative API"? Why would we allow lambdas? If we're allowing expressions, why not statements too? I don't think I get the use case you have in mind.

))]
#[diagnostic(severity(Warning))]
#[diagnostic(code("Qsc.Resolve.Deprecated"))]
Deprecated(String, #[label] Span),
Copy link
Member

Choose a reason for hiding this comment

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

I feel iffy about having this baked into the resolver, I feel like it could be a separate pass, both for future-facing reasons (such as the ability to disable and order checks, as we talked about), but also because it feels like scope creep for the core parts of the compiler.

@swernli swernli marked this pull request as draft October 25, 2023 21:10
@swernli
Copy link
Collaborator Author

swernli commented Oct 25, 2023

@minestarks is asking some great questions, and even pointed out that the current approach doesn't fully update for the existence of a new warning type. For example, the playground and QCOM disable the run button if any errors are present, but check that by just looking at whether the length of VSDiagnostics returned from compilation is greater than zero. We can improve this via filtering (and should) but it might be worth a more holistic look at what a warning means and how it should be surfaced.

I'm changing this to draft to give us the chance to work through these questions and decide if a separate, "prepare for warnings" PR is needed in the infrastructure. I think it might be worth reviewing that on it's own separate from the feature being introduced here.

@swernli
Copy link
Collaborator Author

swernli commented Oct 28, 2023

Ok, after thinking about it some more, I have an approach I'd like to try. I'm going to split this up into two PRs.

The first PR will introduce the Unimplemented attribute and include the refactoring of attribute handling to be enum-based as well as the error reporting for the new Unimplemented attribute. There, we can focus on deciding whether that error makes sense in name resolution or should be pulled out to a pass and make sure the infrastructure for annotating references with relevant attribute info is acceptable.

Then the second, follow-up PR can introduce the Deprecated attribute, which will be the first warning in the new stack. That PR discussion can focus on making sure we agree on how warnings are handled throughout the stack and tools, as well as making sure Deprecated itself is handled in a consistent way to give a good use experience.

@swernli
Copy link
Collaborator Author

swernli commented Oct 30, 2023

part 1 is now up as #826. Closing this in favor of that approach.

@swernli swernli closed this Oct 30, 2023
@swernli swernli deleted the swernli/impl-attrs branch December 13, 2023 19:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants