Skip to content

[WIP] function argument stringification: __ARG_STRING__ (replaced by 7821)#7811

Closed
timotheecour wants to merge 9 commits intodlang:masterfrom
timotheecour:pr_arg_names
Closed

[WIP] function argument stringification: __ARG_STRING__ (replaced by 7821)#7811
timotheecour wants to merge 9 commits intodlang:masterfrom
timotheecour:pr_arg_names

Conversation

@timotheecour
Copy link
Contributor

@timotheecour timotheecour commented Jan 30, 2018

Stringify caller's function argument, analog to C's # stringification macro (except it's type safe).
A main use case is to allow finally writing nice logging and debug tools.

Purely library based alternatives all have drawbacks, see #7811 (comment) for example.

It also works with UFCS.

Eg:

string log(T)(T a, string name=__ARG_STRING__!a)
{
  import std.conv;
  return text(name, ": ", a);
}
assert(log(1+2) == "1 + 2: 3");
int x=3;
assert(log(x) == "x: 3");

NOTE:

  • code calls earg.stringified = earg.toChars; for all function arguments only when attempting to match (via callMatch) against a function that has __ARG_STRING__. Caveat: enum folding will happen before that, so enum x=3; assert(log(x) == "3: 3"); instead of assert(log(x) == "x: 3"); ; but the behavior is correct for non-enum's (eg static const)
  • currently only allows binding to a function parameter (not template parameter, and not variadic parameters); this could be lifted in future PR without breaking backward compatibility

EDIT: closing in favor of #7821

@dlang-bot dlang-bot added the Review:WIP Work In Progress - not ready for review or pulling label Jan 30, 2018
@dlang-bot
Copy link
Contributor

dlang-bot commented Jan 30, 2018

Thanks for your pull request, @timotheecour! We are looking forward to reviewing it, and you should be hearing from a maintainer soon.

Some tips to help speed things up:

  • smaller, focused PRs are easier to review than big ones

  • try not to mix up refactoring or style changes with bug fixes or feature enhancements

  • provide helpful commit messages explaining the rationale behind each change

Bear in mind that large or tricky changes may require multiple rounds of review and revision.

Please see CONTRIBUTING.md for more information.

Bugzilla references

Your PR doesn't reference any Bugzilla issue.

If your PR contains non-trivial changes, please reference a Bugzilla issue or create a manual changelog.

@wilzbach
Copy link
Contributor

wilzbach commented Jan 30, 2018

Wow, I wanted to add this to DMD to, but you beat me to it.
However, I can add further motivation for this. With __ARG_NAME__ we can finally write nice logging and debug tools.
Consider, for example, my stalled PR to Phobos (dlang/phobos#4318) for adding a user-friendly dump method, which never went anywhere because the best we can do at the moment was:

dump!(x, y) // x == 5, y == 6
dump(x, y) // arg.1= 5, arg.2 = 6

Both have severe downsides, __ARG_NAME__ is ideal for this use case.
Other potential areas of use are of course at least:

  • logging (e.g. stdx.logger, vibe.core.logger, ..)
  • better exception messages ("myFancyVar is null" is a lot more helpful than "r is null")

@timotheecour timotheecour changed the title [WIP] function argument stringification: __ARG_NAME__ [WIP] function argument stringification: __ARG_STRING__ Jan 31, 2018
@timotheecour
Copy link
Contributor Author

yes, that's exactly of of the main use cases i had in mind :) will update commit msg to make it clearer

@jacob-carlborg
Copy link
Contributor

jacob-carlborg commented Jan 31, 2018

Please no. This is yet another hack because we don't have AST macros. Yes, I know that we most likely will not get those.

I'll give it a fair review anyway.


Eg:
---
string log(T)(T a, string name=__ARG_NAME__!a)
Copy link
Contributor

Choose a reason for hiding this comment

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

The code implements __ARG_STRING__.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

TOK op; // to minimize use of dynamic_cast
unsigned char size; // # of bytes in Expression so we can copy() it
unsigned char parens; // if this is a parenthesized expression
const(char)* stringified; // for function calls (in case of __ARG_NAME__)
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here, the code implements __ARG_STRING__.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

// Checks that no constant folding happens, cf D20180130T161632.
fun1(1+1+2, `1 + 1 + 2`);
enum t=44;
fun1(t+t+t, `t + t + t`); // want: t+t+t
Copy link
Contributor

Choose a reason for hiding this comment

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

Operators should have spaces on each side.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

@jacob-carlborg
Copy link
Contributor

I would like to see examples/tests what happens in the following scenarios:

  • __ARG_STRING__ is used in a non-parameter context
  • More complex expressions
  • Expressions with user defined types
  • Passing a type to __ARG_STRING__
  • Using __ARG_STRING__ as the default value of a template argument

Copy link
Contributor

@jacob-carlborg jacob-carlborg left a comment

Choose a reason for hiding this comment

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

There are many issues with the code style. These are the corrections:

  • Space after if
  • Spaces around operators
  • else on its own line
  • for -> foreach
  • Four spaces for indentation

src/dmd/tokens.d Outdated
TOK.overloadSet: "__overloadset",
TOK.file: "__FILE__",
TOK.fileFullPath: "__FILE_FULL_PATH__",
TOK.argumentName: "__ARG_STRING__",
Copy link
Contributor

Choose a reason for hiding this comment

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

The naming is inconsistent. The token is called argumentName while the symbol used in the code is called __ARG_STRING__.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

src/dmd/parse.d Outdated
/*****************************************
* Parses default argument initializer expression that is an assign expression,
* with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, and __PRETTY_FUNCTION__.
* with special handling for __FILE__, __FILE_DIR__, __LINE__, __MODULE__, __FUNCTION__, __PRETTY_FUNCTION__, __ARG_NAMES__.
Copy link
Contributor

Choose a reason for hiding this comment

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

__ARG_NAMES__ -> __ARG_STRING__.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done


/***********************************************************
*/
extern (C++) final class ArgnameInitExp : DefaultInitExp
Copy link
Contributor

Choose a reason for hiding this comment

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

Now there are three different names in play here: ArgnameInitExp, __ARG_STRING__ and argumentName. Pick one of them and use it consistently. Please also avoid abbreviations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

changed to more consistent naming:

__ARG_STRING__
argString
ArgStringInitExp
isArgStringInitExp
resolveArgString

I did use commonly used abbreviation though, __ARGUMENT_STRING__ would be too long IMO (and some ppl had suggesed ARG also)

assert(nargs>0);
bool found=false;
// TODO: should Parameters._foreach be used?
for(int u=0;u<nparams;u++)
Copy link
Contributor

Choose a reason for hiding this comment

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

Use foreach instead of for.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done

return;
}

for(int i=0;i<(exp.arguments ? exp.arguments.dim : 0) ; i++){
Copy link
Contributor

Choose a reason for hiding this comment

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

asDArray can be used instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

not used anymore

@jacob-carlborg
Copy link
Contributor

There are commits from other people that are already merged.

@timotheecour
Copy link
Contributor Author

There are commits from other people that are already merged.

fixed

@timotheecour
Copy link
Contributor Author

timotheecour commented Jan 31, 2018

ARG_STRING is used in a non-parameter context

I am disallowing this use case; ARG_STRING is useful mostly as a default function argument. If you have a good use case for that (which one, I'd be curious?) we could always add that later.

Using ARG_STRING as the default value of a template argument

as noted in PR msg, this is on TODO list but could be done in subsequent PR while being backward compatible

More complex expressions
Expressions with user defined types

added this:

fun1(A.init.x + A(a).myfun(), `A(1).x + A(a).myfun()`);

see tests

@jacob-carlborg
Copy link
Contributor

I am disallowing this use case; ARG_STRING is useful mostly as a default function argument. If you have a good use case for that (which one, I'd be curious?) we could always add that later.

I'm not arguing for or a against, you just need a test that enforces the current behavior.

as noted in PR msg, this is on TODO list but could be done in subsequent PR while being backward compatible

Same here, test.

@jacob-carlborg
Copy link
Contributor

I don't want to completely ruin this PR, but what about a __traits instead of this magic template or whatever it is?

pound,

// 239
// 241
Copy link
Contributor

Choose a reason for hiding this comment

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

You only added one token, why does the count go up by two?

Copy link
Contributor Author

@timotheecour timotheecour Feb 1, 2018

Choose a reason for hiding this comment

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

IIRC fileFullPath had been added without upading these numbers in the past, so i also correct that; however I'd super like to just get rid of these numbers, they serve no purpose and get out of sync easily; shd be done in a separate PR though; ok to remove in another PR?

Copy link
Contributor

Choose a reason for hiding this comment

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

Fair enough, just making sure you hadn't typo'd. I think they have some use when trying to look up what Tok you have in some function but I'll leave it up to other to determine what to do.

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 see; in this case one could use instead arbitrary increasing numbers at sparse intervals, it's much more robust (and more stable across different compiler versions)

enum TOK : int
{
    reserved = 0,
    // we can reset numbers arbitrarily
    leftParentheses = 100,
    rightParentheses, // will be 101count)
    leftBracket = 200,
}

@thewilsonator
Copy link
Contributor

How does this work with varadic parameters? what about getting stringification of multiple parameters in a string[]?

@rainers
Copy link
Member

rainers commented Feb 1, 2018

what about a __traits instead

There's even parameters reserved as a __traits parameter, but isn't filled with functionality: https://run.dlang.io/gist/d9ab0db568456f102a1e39af574eae4c?compiler=dmd

@timotheecour timotheecour changed the title [WIP] function argument stringification: __ARG_STRING__ [WIP] function argument stringification: __ARG_STRING__ (replaced by 7821) Feb 1, 2018
@timotheecour
Copy link
Contributor Author

timotheecour commented Feb 1, 2018

I don't want to completely ruin this PR, but what about a __traits instead of this magic template or whatever it is?

after discussion on slack, the only difference with proposed PR would be a minor syntactic change (__ARG_STRING_!a vs __traits(argString, a)); I've discussed pros/cons in #7821 (comment) ; could switch to that if there's consensus

NOTE: I'm closing in favor of #7821 which proposes a more robust implementation that avoids technical issues with not having source code lying around, eg FILE and enum's got folded too early

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Review:WIP Work In Progress - not ready for review or pulling Severity:Bug Fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants