This prevents CheckPlaceholderExpr from being called twice when the following code is compiled, which prevents clang from incorrectly issuing a -Warc-repeated-use-of-weak warning:
void testAuto() { auto __weak wp = NSBundle2.foo2.weakProp; }
Differential D55662
[Sema] If CheckPlaceholderExpr rewrites a placeholder expression when the type of an auto variable is being deduced, use the rewritten expression when performing initialization of the variable. ahatanak on Dec 13 2018, 11:20 AM. Authored by
Details
This prevents CheckPlaceholderExpr from being called twice when the following code is compiled, which prevents clang from incorrectly issuing a -Warc-repeated-use-of-weak warning: void testAuto() { auto __weak wp = NSBundle2.foo2.weakProp; }
Diff Detail
Event TimelineComment Actions I think the right fix here is to call CheckPlaceholderExpr in ActOnDecltypeExpression / HandleExprEvaluationContextForTypeof before the original unevaluated context is exited. You can then change the type-building routines to assert that they aren't given a placeholder. The current ordering probably also causes unwanted behavior with expressions like decltype(&functionTemplate<int>) that should be getting resolved to a single template within the unevaluated context so that the template specialization is not considered ODR-used.
Comment Actions Do you have an example, and is there a reasonable place to call CheckPlaceholderExpr on those paths? Comment Actions Here are a couple of examples I found running the regression tests: int f0(int); float f0(float); decltype(f0) f0_a; // this is not valid. template <class... Ts> int var_expr(Ts... ts); template <class... Ts> auto a_function(Ts... ts) -> decltype(var_expr(ts...)); template <class T> using partial = decltype(a_function<int, T>); int use_partial() { partial<char> n; } template<class T> void oneT() { } int main() { decltype(oneT<int>)* fun = 0; } If the call to CheckPlaceholderExpr in Sema::BuildDecltypeType is moved to TreeTransform<Derived>::TransformDecltypeType and Parser::ParseDecltypeSpecifier, we can assert at the beginning of Sema::BuildDecltypeType. Comment Actions Okay, I'm not really understanding what path goes through those two places but not through ActOnDecltypeExpression, since both of them seem to unconditionally call the latter (at least, in the expression-parsing path). Comment Actions Sorry, please ignore my previous comment. I was looking at the wrong place. The following code reaches Sema::BuildDecltypeType without going through ActOnDecltypeExpression: template <typename T> void overloaded_fn(T); decltype(auto) v5 = &overloaded_fn<int>; Sema::BuildDecltypeType is called from Sema::DeduceAutoType, so calling CheckPlaceholderExpr there should fix the assert when the test case above is compiled. Comment Actions Remove the call to CheckPlaceholderExpr in Sema::BuildDecltypeType and move it to Sema::DeduceAutoType. Assert that the expression that is passed to Sema::BuildDecltypeType isn't a placeholder. Comment Actions Since I'm just moving the call to CheckPlaceholderExpr to the call site, I don't think I have to push an unevaluated context there? Also, it looks like I can just change the check Init->getType()->isNonOverloadPlaceholderType() at the beginning of Sema::DeduceAutoType to Init->getType()->getAsPlaceholderType() instead of inserting the call to CheckPlaceholderExpr right before the call to BuildDecltypeType. Comment Actions Hmm. Right, for the auto inference specifically it's fine because the expression is in fact evaluated: we're not just eliminating placeholders in order to resolve decltype, we're eliminating placeholders to actually figure out what's going on with the initialization.
The reason it's specifically checking for a non-overload placeholder is that normal auto can have additional structure in the declarator that could potentially resolve an overload set without picking a single template specialization. So you can only check for an arbitrary placeholder if this is the decltype(auto) case. But I agree that it still makes sense to only do this once up front, yeah.
Comment Actions clang currently diagnose the repeated use of weak in the following case (with or without this patch): auto __weak wp = b.weakProp; I find this counterintuitive, but I guess this is the expected behavior? Comment Actions Do not record use of weak variables when the type of an auto variable is being deduced. I'm not sure creating an RAII class is the best way to silence the warning, but I haven't been able to come up with something better. Comment Actions The call to isUnevaluatedContext in ObjCPropertyOpBuilder::complete returns false when the type of auto __weak wp in testAuto is being deduced because the ExpressionEvaluationContextRecord on the stack isn't an unevaluated context. I can silence the warning if I can push an unevaluated context at the entry of Sema::DeduceAutoType, but it's not clear to me that it's safe to do so. Comment Actions Hmm. Are we resolving the placeholder expression twice here? That might be the basic problem. Comment Actions Yes, CheckPlaceholderExpr is called in Sema::DeduceAutoType to deduce the type of the auto variable and then called again when InitializationSequence::InitializeFrom generates the Expr for initialization. If we want to avoid calling CheckPlaceholderExpr twice, I think it's possible to cache the results of ObjCPropertyOpBuilder::buildRValueOperation in Sema. Alternatively, we can just pass down a flag that indicates we are deducing auto types and pass it to the constructor of ObjCPropertyOpBuilder. Comment Actions Can we find a way to preserve the rewritten expression out of DeduceAutoType so that the initialization code just happens to never see a placeholder along this path? Comment Actions We can make DeduceVariableDeclarationType return the rewritten expression and replace Init in Sema::AddInitializerToDecl with it. Alternatively, we can keep a set of ObjCPropertyRefExprs passed to recordUseOfWeak (the original ObjCPropertyRefExpr, if it was rebuilt in SemaPseudoObject.cpp) and avoid recording the use of a weak variable if the expression is passed the second time. Comment Actions If it's at all reasonable to just avoid doing the work multiple times, let's do that. It should also avoid duplicate diagnostics if e.g. an overload is resolved to an unavailable function. Comment Actions Make deduceVarTypeFromInitializer and DeduceVariableDeclarationType return the new initialization expression and use it in Sema::AddInitializerToDecl. Add a test case that tests lambda capture with an initializer.
Comment Actions Oh, and please update the commit message to primarily talk about the changes to placeholder checking. You can discuss the impact on the repeated-use-of-weak warning in a follow-up paragraph. Comment Actions Pass Init by reference and use the rewritten expression returned in it to perform variable initialization. Add a test case that tests new expression with auto types. Comment Actions This patch has three parts:
I think I should commit 1 separately from 2 and 3.
|