I have looked at this more closely in terms of the actual code in emtrends(). It boils down to what is demonstrated in the following code:
# comparison of evaluation methods with lazy eval
# a trivial function
foo = function(x, ...)
x^2
# calls foo three ways
foodoo = function(...) {
message("x1 = ", foo(...))
message("x2 = ", do.call("foo", list(...)))
cl = match.call()
cl[[1]] = quote(foo)
message("x3 = ", eval(cl))
}
# test value
val = 3.5
# test runs
foodoo(x = 5)
## x1 = 25
## x2 = 25
## x3 = 25
foodoo(x = val)
## x1 = 12.25
## x2 = 12.25
## x3 = 12.25
# same tests, wrapped in a function...
bar = function(z) {
foodoo(x = z)
}
bar(5)
## x1 = 25
## x2 = 25
## Error in foo(x = z): object 'z' not found
bar(val)
## x1 = 12.25
## x2 = 12.25
## Error in foo(x = z): object 'z' not found
Created on 2020-05-31 by the reprex package (v0.3.0)
The problem the OP experienced is related to that third method of evaluation. In emtrends(), the code matches the call, doctors it up for use by a different function, and then evaluates it. I'm not sure I fully understand all of this, but what is passed in function calls are actually promises, not values; a promose consists of an unevaluated expression and the environment in which it should be evaluated.
In looking at foodoo(), it appears that, with the first two methods, those promises are updated to the current environment before foo is called, whereas in the third method, the original call is kept intact so that the promises are not changed. And apparently (I'm still not quite sure why), those original environments are good enough when we call foodoo() from the global environment, but obviously not good enough when called from a different environment. If anybody cares to comment and explain further, I'm all ears.
Anyway, I am happy because I was able to fix the problem by forcing evaluation of all ... arguments and replacing them in cl:
# new foodoo
foodoo = function(...) {
message("x1 = ", foo(...))
message("x2 = ", do.call("foo", list(...)))
cl = match.call()
cl[[1]] = quote(foo)
dots = eval(list(...)) # added
cl[names(dots)] = dots # added
message("x3 = ", eval(cl))
}
bar(5)
## x1 = 25
## x2 = 25
## x3 = 25
bar(val)
## x1 = 12.25
## x2 = 12.25
## x3 = 12.25