Skip to content

std.traits: hasFunctionAttributes#4287

Merged
JackStouffer merged 1 commit intodlang:masterfrom
wilzbach:traits_has_function_attributes
Jan 4, 2017
Merged

std.traits: hasFunctionAttributes#4287
JackStouffer merged 1 commit intodlang:masterfrom
wilzbach:traits_has_function_attributes

Conversation

@wilzbach
Copy link
Contributor

@wilzbach wilzbach commented May 7, 2016

As discussed in #4274 the std.traits API for querying after function attributes is not very user-friendly.

functionAttributes!((int a) { }) == (FunctionAttributes.pure_ | FunctionAttributes.nothrow_ | FunctionAttributes.nogc | FunctionAttributes.safe

wouldn't it be nicer, if we have a dedicated function for this?

hasFunctionAttributes!((int a) { }, "pure", "nothrow", "@nogc", "@safe"); 

Imho this makes it a lot more intuitive for the user to read and easier for us to maintain.
I heard about a DIP to cleanup and unify function attributes, so having a function allows us to stay flexible for the future ;-)
Btw the trait getFunctionAttributes already returns a string tuple.

Ping @ntrel

std/traits.d Outdated
See_Also:
$(LREF functionAttributes)
*/
template hasFunctionAttributes(func...)
Copy link
Contributor

Choose a reason for hiding this comment

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

(alias func, strAttributes...)

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 tried this too, but it doesn't work when called with typeof(func)

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe add an overload for (Func, strAttributes...) then? That's best, otherwise at least rename func to args. (It's pretty annoying alias doesn't accept all types).

@JackStouffer
Copy link
Contributor

@wilzbach Issue 14835 rears its ugly head once again.

std/traits.d Outdated

Params:
func = function to check
attributes = variadic number of function attributes
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to state these are strings, not enums.

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 added "... as strings" - do you prefer a different wording?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think that's OK, thanks.

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch 2 times, most recently from f8db352 to 6e0aadd Compare May 9, 2016 16:24
std/traits.d Outdated
$(LREF functionAttributes)
*/
template hasFunctionAttributes(args...)
if (isCallable!(args[0]) && args.length > 0)
Copy link
Contributor

Choose a reason for hiding this comment

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

What about

if (isCallable!(args[0]) && args.length > 1 && allSatisfy!(isSomeString, args[1 .. $]))

Copy link
Contributor Author

Choose a reason for hiding this comment

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

args are just strings, but with a small wrapper this works well. thanks a lot!

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 6e0aadd to 4169827 Compare May 10, 2016 19:53
@wilzbach
Copy link
Contributor Author

As this exposes a public symbol, it needs the andralex tag ;-)

std/traits.d Outdated
See_Also:
$(LREF functionAttributes)
*/
/// ditto
Copy link
Contributor

Choose a reason for hiding this comment

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

Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh that was just a relict from trying to have different overloads. Thanks for spotting that :)

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 4169827 to 556bfd1 Compare May 10, 2016 20:46
@wilzbach
Copy link
Contributor Author

In a similar manner, we could also provide a better API for ParameterStorageClassTuple. Again most of them, but not all need underscores.

std/traits.d Outdated
return res;
}

private enum bool isSomeStringInfer(alias T) = isSomeString!(typeof(T));
Copy link
Contributor

Choose a reason for hiding this comment

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

Private templates should never show up in public template constraints.

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 556bfd1 to dcb8ba4 Compare June 2, 2016 20:30
std/traits.d Outdated
&& allSatisfy!(isSomeString, typeof(args[1 .. $])))
{
enum bool hasFunctionAttributes = {
import std.algorithm.searching: canFind;
Copy link
Contributor

Choose a reason for hiding this comment

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

space before :

Copy link
Contributor Author

Choose a reason for hiding this comment

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

rebased ;-)

@wilzbach
Copy link
Contributor Author

wilzbach commented Jun 2, 2016

I think you can use allSatisfy!(isSomeString, typeof(args[1..$])).

thanks @ntrel - that worked!
(isSomeStringInfer was an relict from the initial version when the function has two specializations)

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from dcb8ba4 to 1254ac5 Compare June 2, 2016 20:43
@JackStouffer
Copy link
Contributor

I don't see any other problems. LGTM

@andralex
Copy link
Member

Breathtaking, thx.

  1. Please add an example checking the attributes of a function instantiated from a template. That should show that attributes are deduced even if not introduced by the user. Use typeof(fncall(args)) as the first argument to hasFunctionAttributes.
  2. Can this be generalized? I'm thinking if one adds some attribute @Crap(42) then subsequently hasFunctionAttribute!(fun, Crap(42)) should yield true.

@andralex
Copy link
Member

I preapprove if (1) is addressed and (2) is at least not precluded by the current design

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 1254ac5 to 2565349 Compare June 19, 2016 16:33
@wilzbach
Copy link
Contributor Author

Please add an example checking the attributes of a function instantiated from a template. That should show that attributes are deduced even if not introduced by the user. Use typeof(fncall(args)) as the first argument to hasFunctionAttributes.

ehm isn't typeof(myFunc(bool)) == bool. I added an example to the ddoced unittest that shows that the attributes are deduced by the compiler.

In the huge tests there are also cases where attributes are inferred, e.g. @system and the delegates at the bottom are inferred too.

Can this be generalized? I'm thinking if one adds some attribute @crap(42) then subsequently hasFunctionAttribute!(fun, Crap(42)) should yield true.

Yes it could be, but the DLang specification makes an explicit difference between UDA and FunctionAttributes.
Moreover there is already hasUDA. I will have a look whether hasUDA can be improved ;-)

@wilzbach
Copy link
Contributor Author

I preapprove if (1) is addressed and (2) is at least not precluded by the current design
..
isn't typeof(myFunc(bool)) == bool. I added an example to the ddoced unittest that shows that the attributes are deduced by the compiler.
..
Yes it could be, but the DLang specification makes an explicit difference between UDA and FunctionAttributes.

@9il could you please point out why you marked this as "needs work"? I don't see what is blocking on my side & it has already been preapproved.
As usual to avoid unnecessary conflicts & work, I will add the changelog entry afterwards.

@9il
Copy link
Member

9il commented Aug 28, 2016

@9il could you please point out why you marked this as "needs work"? I don't see what is blocking on my side & it has already been preapproved.

auto tester is failing

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 2565349 to 87cadb4 Compare August 28, 2016 16:53
@dlang-bot
Copy link
Contributor

@wilzbach, thanks for your PR! By analyzing the annotation information on this pull request, we identified @9rnsr, @sinfu and @WalterBright to be potential reviewers. @9rnsr: The PR was automatically assigned to you, please reassign it if you were identified mistakenly.

(The DLang Bot is under development. If you experience any issues, please open an issue at its repo.)

@wilzbach
Copy link
Contributor Author

auto tester is failing

Oh sorry - it looks like DMD got smarter and can now automatically infer @safe for free functions (it doesn't do this for structs yet).

int safeF() @safe { return 0; }

int pureF() pure { return 0; }
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

CodeCov is failing because DMD counts all these functions as uncovered.
(there's a NG thread discussion about not counting unittest blocks, but tl:dr it won't happen)

@wilzbach
Copy link
Contributor Author

I found this PR -> rebased + squashed it -> it's alive :)

I preapprove if (1) is addressed and (2) is at least not precluded by the current design

For clarity here again my answers to Andrei's points:

(1): Please add an example checking the attributes of a function instantiated from a template. That should show that attributes are deduced even if not introduced by the user. Use typeof(fncall(args)) as the first argument to hasFunctionAttributes.

As mentioned above I added:

bool myFunc(T)(T b)  { return !b;  }
static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow"));

However typeof(myFunc(true)) doesn't work because it is bool.

(2) Can this be generalized? I'm thinking if one adds some attribute @Crap(42) then subsequently hasFunctionAttribute!(fun, Crap(42)) should yield true.

Yes it could be, but the DLang specification makes an explicit difference between UDA and FunctionAttributes.
Moreover there is already hasUDA.

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 87cadb4 to 936380b Compare December 10, 2016 19:54
@wilzbach wilzbach added this to the 2.073.0-b0 milestone Dec 29, 2016
@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from 936380b to 371089d Compare January 4, 2017 12:39
@wilzbach
Copy link
Contributor Author

wilzbach commented Jan 4, 2017

I had to rebase this PR because I added a changelog entry (btw there were tabs in the changelog file).

Is anything else blocking this?
(Andrei preapproved the name)

std/traits.d Outdated
{
if (!funcAttribs.canFind(attribute))
{
b = false;
Copy link
Member

Choose a reason for hiding this comment

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

Eh, that's overly complicated. Why not just return false from here?

std/traits.d Outdated
break;
}
}
return b;
Copy link
Member

Choose a reason for hiding this comment

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

(and true from here)

std/traits.d Outdated
{
real func(real x) pure nothrow @safe
{
return x;
Copy link
Member

Choose a reason for hiding this comment

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

In order to avoid false coverage reports, you may want to just declare the function without implementing it.

std/traits.d Outdated
{
struct S
{
int noF() { return 0; }
Copy link
Member

Choose a reason for hiding this comment

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

same here - all are not covered

struct S2
{
int pure_const() const pure { return 0; }
int pure_sharedconst() const shared pure { return 0; }
Copy link
Member

Choose a reason for hiding this comment

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

these, too - declare and don't define

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from c885f25 to ac81a9c Compare January 4, 2017 13:43
}
static assert(hasFunctionAttributes!(myFunc!bool, "@safe", "pure", "@nogc", "nothrow"));
static assert(!hasFunctionAttributes!(myFunc!bool, "@trusted"));
static assert(!hasFunctionAttributes!(myFunc!bool, "shared"));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This change is just to make the public unittest a bit more interesting and not use @trusted twice.

@wilzbach
Copy link
Contributor Author

wilzbach commented Jan 4, 2017

Eh, that's overly complicated. Why not just return false from here?

When this PR initially was submitted a DMD bug prevented this (it wouldn't recognize this properly and warn with "unreachable code"). However, it seems that this has been fixed. Thanks! :)

In order to avoid false coverage reports, you may want to just declare the function without implementing it.

Ok, I did this for the manually annotated functions. However, the other functions are supposed to check whether automatic attribute inference works correctly. Thus, I added a few dummy calls.

std/traits.d Outdated
import std.algorithm.searching : canFind;
import std.range : only;
enum funcAttribs = only(__traits(getFunctionAttributes, args[0]));
foreach (attribute; args[1..$])
Copy link
Member

Choose a reason for hiding this comment

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

spaces around .. (should be detected by the style checker...)

@wilzbach wilzbach force-pushed the traits_has_function_attributes branch from ac81a9c to 970dff3 Compare January 4, 2017 14:26
@wilzbach
Copy link
Contributor Author

wilzbach commented Jan 4, 2017

spaces around .. (should be detected by the style checker...)

Yeah this is on my backlog. Btw while I like standardization it's not uniformly considered so, see e.g. #4999

@wilzbach
Copy link
Contributor Author

wilzbach commented Jan 4, 2017

spaces around .. (should be detected by the style checker...)

I added the whitespace, rebased and armed the new merge-bot.

@JackStouffer
Copy link
Contributor

Auto-merge toggled on

@JackStouffer JackStouffer merged commit f94a9e9 into dlang:master Jan 4, 2017
@wilzbach wilzbach deleted the traits_has_function_attributes branch February 19, 2017 23:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants