Quuxplusone / LLVMBugzillaTest

0 stars 0 forks source link

assume_nonnull confused about addresses of @selectors: Assertion failed: (E->isGLValue() || E->getType()->isFunctionType() || E->getType()->isVoidType()) #24773

Closed Quuxplusone closed 9 years ago

Quuxplusone commented 9 years ago
Bugzilla Link PR24774
Status RESOLVED FIXED
Importance P normal
Reported by Nico Weber (nicolasweber@gmx.de)
Reported on 2015-09-10 12:45:16 -0700
Last modified on 2015-09-15 18:17:57 -0700
Version trunk
Hardware PC All
CC dgregor@apple.com, fjahanian@apple.com, jroelofs@jroelofs.com, llvm-bugs@lists.llvm.org
Fixed by commit(s)
Attachments clang-addsel-lval.diff (5483 bytes, text/plain)
clang-addsel-rval.diff (2960 bytes, text/plain)
clang-addsel-bandaid.diff (2156 bytes, text/plain)
Blocks
Blocked by
See also
Reduced:

ummer:GTMWindowSheetController-9369d4 thakis$ cat GTMWindowSheetController-
9369d4.m
#define NS_ASSUME_NONNULL_BEGIN _Pragma("clang assume_nonnull begin")
#define NS_ASSUME_NONNULL_END   _Pragma("clang assume_nonnull end")
NS_ASSUME_NONNULL_BEGIN
@interface Foo
- (void)setArgument:(void *)argumentLocation;
@end
NS_ASSUME_NONNULL_END

@interface GTMWindowSheetController
@end
@implementation GTMWindowSheetController
- (void)beginSystemSheet:(Foo*)invocation {
  [invocation setArgument:&@selector(foo:)];
}
@end
hummer:GTMWindowSheetController-9369d4 thakis$ /Users/thakis/src/llvm-
build/bin/clang -c GTMWindowSheetController-9369d4.m
Assertion failed: (E->isGLValue() || E->getType()->isFunctionType() || E-
>getType()->isVoidType()), function EvaluateLValue, file /Users/thakis/src/llvm-
svn/tools/clang/lib/AST/ExprConstant.cpp, line 4565.
0  clang-3.8                0x0000000103aaa909
llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 57
1  clang-3.8                0x0000000103aa9e26 llvm::sys::RunSignalHandlers() +
70
2  clang-3.8                0x0000000103aab485 SignalHandler(int) + 645
3  libsystem_platform.dylib 0x00007fff94722f1a _sigtramp + 26
4  libsystem_platform.dylib 0x70632e746e617473 _sigtramp + 3656336755
5  clang-3.8                0x0000000103aab156 abort + 22
6  clang-3.8                0x0000000103aab131 __assert_rtn + 81
7  clang-3.8                0x0000000104ef0e46 EvaluateLValue(clang::Expr
const*, (anonymous namespace)::LValue&, (anonymous namespace)::EvalInfo&) + 230
8  clang-3.8                0x0000000104f20900
clang::StmtVisitorBase<clang::make_const_ptr, (anonymous
namespace)::PointerExprEvaluator, bool>::Visit(clang::Stmt const*) + 368
9  clang-3.8                0x0000000104f2360f (anonymous
namespace)::PointerExprEvaluator::VisitCastExpr(clang::CastExpr const*) + 463
10 clang-3.8                0x0000000104f20ecc
clang::StmtVisitorBase<clang::make_const_ptr, (anonymous
namespace)::PointerExprEvaluator, bool>::Visit(clang::Stmt const*) + 1852
11 clang-3.8                0x0000000104ef484c Evaluate(clang::APValue&,
(anonymous namespace)::EvalInfo&, clang::Expr const*) + 828
12 clang-3.8                0x0000000104ef0591 EvaluateAsRValue((anonymous
namespace)::EvalInfo&, clang::Expr const*, clang::APValue&) + 97
13 clang-3.8                0x0000000104ef031f
clang::Expr::EvaluateAsRValue(clang::Expr::EvalResult&, clang::ASTContext
const&) const + 367
14 clang-3.8                0x0000000104ef06ae
clang::Expr::EvaluateAsBooleanCondition(bool&, clang::ASTContext const&) const
+ 62
15 clang-3.8                0x0000000104721efd CheckNonNullExpr(clang::Sema&,
clang::Expr const*) + 349
16 clang-3.8                0x0000000104717a1f
clang::Sema::checkCall(clang::NamedDecl*, clang::FunctionProtoType const*,
llvm::ArrayRef<clang::Expr const*>, bool, clang::SourceLocation,
clang::SourceRange, clang::Sema::VariadicCallType) + 2767
17 clang-3.8                0x000000010471c932
clang::Sema::CheckObjCMethodCall(clang::ObjCMethodDecl*, clang::SourceLocation,
llvm::ArrayRef<clang::Expr const*>) + 98
18 clang-3.8                0x000000010495cf96
clang::Sema::CheckMessageArgumentTypes(clang::QualType,
llvm::MutableArrayRef<clang::Expr*>, clang::Selector,
llvm::ArrayRef<clang::SourceLocation>, clang::ObjCMethodDecl*, bool, bool,
clang::SourceLocation, clang::SourceLocation, clang::SourceRange,
clang::QualType&, clang::ExprValueKind&) + 3062
19 clang-3.8                0x000000010496162b
clang::Sema::BuildInstanceMessage(clang::Expr*, clang::QualType,
clang::SourceLocation, clang::Selector, clang::ObjCMethodDecl*,
clang::SourceLocation, llvm::ArrayRef<clang::SourceLocation>,
clang::SourceLocation, llvm::MutableArrayRef<clang::Expr*>, bool) + 4395
20 clang-3.8                0x0000000104964b78
clang::Sema::ActOnInstanceMessage(clang::Scope*, clang::Expr*, clang::Selector,
clang::SourceLocation, llvm::ArrayRef<clang::SourceLocation>,
clang::SourceLocation, llvm::MutableArrayRef<clang::Expr*>) + 520
21 clang-3.8                0x000000010451f066
clang::Parser::ParseObjCMessageExpressionBody(clang::SourceLocation,
clang::SourceLocation, clang::OpaquePtr<clang::QualType>, clang::Expr*) + 3030
22 clang-3.8                0x000000010451e411
clang::Parser::ParseObjCMessageExpression() + 1009
23 clang-3.8                0x00000001044f0ee1
clang::Parser::ParseCastExpression(bool, bool, bool&,
clang::Parser::TypeCastState) + 5937
24 clang-3.8                0x00000001044ec966
clang::Parser::ParseAssignmentExpression(clang::Parser::TypeCastState) + 134
25 clang-3.8                0x00000001044ec8be
clang::Parser::ParseExpression(clang::Parser::TypeCastState) + 14
26 clang-3.8                0x00000001045317c3
clang::Parser::ParseExprStatement() + 51
27 clang-3.8                0x0000000104530f2a
clang::Parser::ParseStatementOrDeclarationAfterAttributes(llvm::SmallVector<clang::Stmt*,
32u>&, bool, clang::SourceLocation*, clang::Parser::ParsedAttributesWithRange&)
+ 3418
28 clang-3.8                0x000000010453013b
clang::Parser::ParseStatementOrDeclaration(llvm::SmallVector<clang::Stmt*,
32u>&, bool, clang::SourceLocation*) + 155
29 clang-3.8                0x000000010453817f
clang::Parser::ParseCompoundStatementBody(bool) + 1807
30 clang-3.8                0x00000001045389f2
clang::Parser::ParseFunctionStatementBody(clang::Decl*,
clang::Parser::ParseScope&) + 194
31 clang-3.8                0x0000000104519556
clang::Parser::ParseLexedObjCMethodDefs(clang::Parser::LexedMethod&, bool) + 342
32 clang-3.8                0x0000000104511ace
clang::Parser::ObjCImplParsingDataRAII::finish(clang::SourceRange) + 94
33 clang-3.8                0x0000000104510cdd
clang::Parser::ParseObjCAtEndDeclaration(clang::SourceRange) + 141
34 clang-3.8                0x000000010454cd2e
clang::Parser::ParseExternalDeclaration(clang::Parser::ParsedAttributesWithRange&,
clang::ParsingDeclSpec*) + 1182
35 clang-3.8                0x00000001045107b0
clang::Parser::ParseObjCAtImplementationDeclaration(clang::SourceLocation) +
2704
36 clang-3.8                0x000000010454cd2e
clang::Parser::ParseExternalDeclaration(clang::Parser::ParsedAttributesWithRange&,
clang::ParsingDeclSpec*) + 1182
37 clang-3.8                0x000000010454c75d
clang::Parser::ParseTopLevelDecl(clang::OpaquePtr<clang::DeclGroupRef>&) + 525
38 clang-3.8                0x00000001044aff75 clang::ParseAST(clang::Sema&,
bool, bool) + 501
39 clang-3.8                0x0000000103f54c0b clang::FrontendAction::Execute()
+ 75
40 clang-3.8                0x0000000103f1ccbb
clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 843
41 clang-3.8                0x0000000103f97a21
clang::ExecuteCompilerInvocation(clang::CompilerInstance*) + 4033
42 clang-3.8                0x000000010287c532 cc1_main(llvm::ArrayRef<char
const*>, char const*, void*) + 1474
43 clang-3.8                0x000000010287ad80 main + 12560
44 libdyld.dylib            0x00007fff90bc65c9 start + 1
45 libdyld.dylib            0x0000000000000031 start + 1866701417
Quuxplusone commented 9 years ago

class PointerExprEvaluator in ExprConstant.cpp says "// FIXME: Missing: @protocol, @selector", that looks related...

Quuxplusone commented 9 years ago
smaller repro:

_Pragma("clang assume_nonnull begin")
void setArgument(void* argumentLocation);
_Pragma("clang assume_nonnull end")

void beginSystemSheet() {
  setArgument(&@selector(foo:));
}
Quuxplusone commented 9 years ago
smaller:

void f(void* _Nonnull argumentLocation);
void g() {
  f(&@selector(foo:));
}
Quuxplusone commented 9 years ago
Making @selectors lvalues (like string literals are) makes this go, but it
doesn't seem like the right direction (also breaks a few tests, but that
generally looks fixable):

Index: include/clang/AST/ExprObjC.h
===================================================================
--- include/clang/AST/ExprObjC.h    (revision 247293)
+++ include/clang/AST/ExprObjC.h    (working copy)
@@ -405,7 +405,7 @@
 public:
   ObjCSelectorExpr(QualType T, Selector selInfo,
                    SourceLocation at, SourceLocation rp)
-    : Expr(ObjCSelectorExprClass, T, VK_RValue, OK_Ordinary, false, false,
+    : Expr(ObjCSelectorExprClass, T, VK_LValue, OK_Ordinary, false, false,
            false, false),
     SelName(selInfo), AtLoc(at), RParenLoc(rp){}
   explicit ObjCSelectorExpr(EmptyShell Empty)
Index: lib/AST/ExprClassification.cpp
===================================================================
--- lib/AST/ExprClassification.cpp  (revision 247293)
+++ lib/AST/ExprClassification.cpp  (working copy)
@@ -137,6 +137,8 @@
   case Expr::FunctionParmPackExprClass:
   case Expr::MSPropertyRefExprClass:
   case Expr::OMPArraySectionExprClass:
+// XXX
+  case Expr::ObjCSelectorExprClass:
     return Cl::CL_LValue;

     // C99 6.5.2.5p5 says that compound literals are lvalues.
@@ -170,7 +172,6 @@
   case Expr::TypeTraitExprClass:
   case Expr::ArrayTypeTraitExprClass:
   case Expr::ExpressionTraitExprClass:
-  case Expr::ObjCSelectorExprClass:
   case Expr::ObjCProtocolExprClass:
   case Expr::ObjCStringLiteralClass:
   case Expr::ObjCBoxedExprClass:
Quuxplusone commented 9 years ago

Related: PR7390, r106242, r119685

Quuxplusone commented 9 years ago

Maybe we want something like r180866 for &@selector

Quuxplusone commented 9 years ago
This seems to do the trick:

hummer:clang thakis$ svn diff
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp   (revision 247293)
+++ lib/Sema/SemaExpr.cpp   (working copy)
@@ -9893,7 +9893,11 @@
     OrigOp = op = new (Context)
         MaterializeTemporaryExpr(op->getType(), OrigOp.get(), true);
   } else if (isa<ObjCSelectorExpr>(op)) {
-    return Context.getPointerType(op->getType());
+    // @selector() is an rvalue but can have its address taken.  Materialize
+    // it instead of returning a pointer type so that the subexpression of an
+    // UnaryAddrOf has fewer special cases.
+    OrigOp = op = new (Context)
+        MaterializeTemporaryExpr(op->getType(), OrigOp.get(), true);
   } else if (lval == Expr::LV_MemberFunction) {
     // If it's an instance method, make a member pointer.
     // The expression must have exactly the form &A::foo.
Quuxplusone commented 9 years ago

…but it probably makes us generate the wrong code :-) I'll look into adding some test coverage for that first, I suppose.

Quuxplusone commented 9 years ago

After some further thinking, making @selector expressions lvalues seems like a pretty good idea (it's similar to typeid), so comment 4 looks pretty good.

It has the problem that it makes this compile:

@selector(dealloc) = s;

This could be fixed by giving @selector a const type:

Index: lib/Sema/SemaExprObjC.cpp

--- lib/Sema/SemaExprObjC.cpp (revision 247709) +++ lib/Sema/SemaExprObjC.cpp (working copy) @@ -1210,7 +1210,7 @@ break; } }

But then SEL* ps2 = &@selector(dealloc); starts warning "cannot initialize a variable of type 'SEL ' with an rvalue of type 'const SEL '".

(SEL can't internally be a const pointer to internal type:

$ svn diff lib/AST Index: lib/AST/ASTContext.cpp

--- lib/AST/ASTContext.cpp (revision 247709) +++ lib/AST/ASTContext.cpp (working copy) @@ -6006,7 +6006,7 @@

TypedefDecl *ASTContext::getObjCSelDecl() const { if (!ObjCSelDecl) {

Then SEL t; t = @selector(foo); doesn't build, of course. And making SEL a pointer to const internal type doesn't help with @selector(foo) = 0; and also has mangling implications.)

Quuxplusone commented 9 years ago
Maybe emitting a diagnostic about "can't convert const SEL* to SEL*" ain't so
bad: Currently, this compiles without warnings and aborts at runtime:

#import <Foundation/Foundation.h>

@interface F : NSObject
@end
@implementation F
- (void)foo {
  printf("hi\n");
}
@end

void g(SEL *s) { *s = 0; }
int main() {
  g(&@selector(foo));
  [[[F alloc] init] foo];
}
Quuxplusone commented 9 years ago
This is the motivation for this bug, btw:

#import <Foundation/Foundation.h>

@interface Ind : NSObject
@end
@implementation Ind
- (void)delay:(SEL)s {
  [self performSelector:s];
}

- (void)foo {
  printf("hi\n");
}
@end

int main() {
  Ind* ind = [[Ind alloc] init];
  SEL theSelector;
  NSMethodSignature *aSignature;
  NSInvocation *anInvocation;

  theSelector = @selector(delay:);
  aSignature = [Ind instanceMethodSignatureForSelector:theSelector];
  anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
  [anInvocation setSelector:theSelector];
  [anInvocation setTarget:ind];
  [anInvocation setArgument:&@selector(foo) atIndex:2];

  [anInvocation invokeWithTarget:ind];
}

With the lvalue patch, this warns:

testinv.m:26:29: warning: sending 'const SEL *' to parameter of type 'void *'
discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
  [anInvocation setArgument:&@selector(foo) atIndex:2];
                            ^~~~~~~~~~~~~~~
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk/System/Library/Frameworks/Foundation.framework/Headers/NSInvocation.h:34:29:
note: passing argument to parameter 'argumentLocation'
      here
- (void)setArgument:(void *)argumentLocation atIndex:(NSInteger)idx;
                            ^
Quuxplusone commented 9 years ago
The "real rvalue" approach is fine even with a slightly trickier variant that
looks like so:

#import <Foundation/Foundation.h>

@interface Ind : NSObject
@end
@implementation Ind
- (void)delay:(SEL)s {
  [self performSelector:s];
}

- (void)foo {
  printf("hi\n");
}
@end

NSInvocation* getinv(Ind* ind)  __attribute__((noinline)){
  SEL theSelector;
  NSMethodSignature *aSignature;
  NSInvocation *anInvocation;

  theSelector = @selector(delay:);
  aSignature = [Ind instanceMethodSignatureForSelector:theSelector];
  anInvocation = [NSInvocation invocationWithMethodSignature:aSignature];
  [anInvocation setSelector:theSelector];
  [anInvocation setTarget:ind];
  [anInvocation setArgument:&@selector(foo) atIndex:2];
  return anInvocation;
}

char f() __attribute__((noinline)) {
  char c[80];
  for (int i = 0; i < 80; ++i) c[i] = i;
  return c[9];
}

int main() {
  Ind* ind = [[Ind alloc] init];
  NSInvocation* i = getinv(ind);
  int r = f();
  [i invokeWithTarget:ind];
  return r;
}

That's because setArgument: docs say "This method copies the contents of buffer
as the argument at index. The number of bytes copied is determined by the
argument size."
Quuxplusone commented 9 years ago

Attached clang-addsel-lval.diff (5483 bytes, text/plain): approach 1: make @selector real lvalues

Quuxplusone commented 9 years ago

Attached clang-addsel-rval.diff (2960 bytes, text/plain): approach 2: make @selector real rvalues

Quuxplusone commented 9 years ago

Attached clang-addsel-bandaid.diff (2156 bytes, text/plain): approach 3: let constexpr evaluator handle current hybrid state

Quuxplusone commented 9 years ago

Currently, ObjCSelectorExpr is an rvalue but it can get its address taken. That's a bit awkward and leads to several problems.

The obvious approach is to make the AST less awkward.

Approach 1: Make @selector a real lvalue, and make it const so that @selector(foo) = 0 doesn't compile. Fixes 1. Fixes 4. Fixes 3 (still crashes, but warns...also see below). Fixes 2 (returning &@selector no longer warns). Does not change codegen behavior. Does change warnings:

SEL* s = &@selector(foo);

previously would compile without warnings. Now it warns "can't convert const SEL* to SEL". In particular,

[anInvocation setArgument:&@selector(foo) atIndex:2];

now warns, which is probably the reason for &@selector working in the first place.

Approach 2: Make @selector a real rvalue, and materialize it into a temporary when used. Fixes 1. Fixes 3 (doesn't warn, no longer crashes -- only a copy gets overwritten). Changes codegen behavior: Returning &@selector from a function now really returns the address of a local (but we already warn on this). When passing &@selector in a parameter, this is now the address of a temporary too, silently. For -[NSInvocation setArgument:], that's ok, it's guaranteed to make a copy. We could emit a warning in this case too, but if every use of &@selector causes a warning, why support it at all? (This seems like a fairly decent approach, but it either silently changes codegen behavior, or makes us warn on all most uses of &@selector). If this is done, 4 could be fixed in a follow-up patch.

So neither option is super super appealing and "make AST less awkward" is less attractive than it seems at first sight.

Approach 3: Fix the one StmtVisitor that crashes. Does fix 1 but doesn't help with 2-4.

I'm going to check in approach 3 as it fixes this crash and preserves the status quo. If anyone has strong feelings if we should do 1 or 2, please speak up.

Quuxplusone commented 9 years ago

r247740