diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2011-07-17 15:40:56 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2011-07-17 15:40:56 +0000 |
commit | 180abc3db9ae3b4fc63cd65b15697e6ffcc8a657 (patch) | |
tree | 2097d084eb235c0b12c0bff3445f4ec7bbaa8a12 | |
parent | 29cafa66ad3878dbb9f82615f19fa0bded2e443c (diff) |
Vendor import of clang trunk r135360:vendor/clang/clang-r135360
Notes
Notes:
svn path=/vendor/clang/dist/; revision=224135
svn path=/vendor/clang/clang-r135360/; revision=224136; tag=vendor/clang/clang-r135360
788 files changed, 45084 insertions, 11164 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index aa89823e76f8..3ad60eaff516 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,11 @@ if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) endif() if( NOT EXISTS "${CLANG_PATH_TO_LLVM_BUILD}/bin/tblgen${CMAKE_EXECUTABLE_SUFFIX}" ) - message(FATAL_ERROR "Please set CLANG_PATH_TO_LLVM_BUILD to a directory containing a LLVM build.") + # Looking for bin/Debug/tblgen is a complete hack. How can we get + # around this? + if( NOT EXISTS "${CLANG_PATH_TO_LLVM_BUILD}/bin/Debug/tblgen${CMAKE_EXECUTABLE_SUFFIX}" ) + message(FATAL_ERROR "Please set CLANG_PATH_TO_LLVM_BUILD to a directory containing a LLVM build.") + endif() endif() list(APPEND CMAKE_MODULE_PATH "${CLANG_PATH_TO_LLVM_BUILD}/share/llvm/cmake") @@ -42,7 +46,12 @@ if( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR ) include_directories("${PATH_TO_LLVM_BUILD}/include" "${LLVM_MAIN_INCLUDE_DIR}") link_directories("${PATH_TO_LLVM_BUILD}/lib") - set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/tblgen") + if( EXISTS "${CLANG_PATH_TO_LLVM_BUILD}/bin/tblgen${CMAKE_EXECUTABLE_SUFFIX}" ) + set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/tblgen") + else() + # FIXME: This is an utter hack. + set(LLVM_TABLEGEN_EXE "${PATH_TO_LLVM_BUILD}/bin/Debug/tblgen") + endif() set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) diff --git a/INPUTS/cfg-big-switch.c b/INPUTS/cfg-big-switch.c new file mode 100644 index 000000000000..a48b51a2e301 --- /dev/null +++ b/INPUTS/cfg-big-switch.c @@ -0,0 +1,27 @@ +#define EXPAND_2_CASES(i, x, y) CASE(i, x, y); CASE(i + 1, x, y); +#define EXPAND_4_CASES(i, x, y) EXPAND_2_CASES(i, x, y) EXPAND_2_CASES(i + 2, x, y) +#define EXPAND_8_CASES(i, x, y) EXPAND_4_CASES(i, x, y) EXPAND_4_CASES(i + 4, x, y) +#define EXPAND_16_CASES(i, x, y) EXPAND_8_CASES(i, x, y) EXPAND_8_CASES(i + 8, x, y) +#define EXPAND_32_CASES(i, x, y) EXPAND_16_CASES(i, x, y) EXPAND_16_CASES(i + 16, x, y) +#define EXPAND_64_CASES(i, x, y) EXPAND_32_CASES(i, x, y) EXPAND_32_CASES(i + 32, x, y) +#define EXPAND_128_CASES(i, x, y) EXPAND_64_CASES(i, x, y) EXPAND_64_CASES(i + 64, x, y) +#define EXPAND_256_CASES(i, x, y) EXPAND_128_CASES(i, x, y) EXPAND_128_CASES(i + 128, x, y) +#define EXPAND_512_CASES(i, x, y) EXPAND_256_CASES(i, x, y) EXPAND_256_CASES(i + 256, x, y) +#define EXPAND_1024_CASES(i, x, y) EXPAND_512_CASES(i, x, y) EXPAND_512_CASES(i + 512, x, y) +#define EXPAND_2048_CASES(i, x, y) EXPAND_1024_CASES(i, x, y) EXPAND_1024_CASES(i + 1024, x, y) +#define EXPAND_4096_CASES(i, x, y) EXPAND_2048_CASES(i, x, y) EXPAND_2048_CASES(i + 2048, x, y) + +// This has a *monstrous* single fan-out in the CFG, across 8000 blocks inside +// the while loop. +unsigned cfg_big_switch(int x) { + unsigned y = 0; + while (x > 0) { + switch(x) { +#define CASE(i, x, y) \ + case i: { int case_var = 3*x + i; y += case_var - 1; break; } +EXPAND_4096_CASES(0, x, y); + } + --x; + } + return y; +} diff --git a/INPUTS/cfg-long-chain1.c b/INPUTS/cfg-long-chain1.c new file mode 100644 index 000000000000..2f55b2ada205 --- /dev/null +++ b/INPUTS/cfg-long-chain1.c @@ -0,0 +1,20 @@ +#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y); +#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y) +#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y) +#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y) +#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y) +#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y) +#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y) +#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y) +#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y) +#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y) +#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y) +#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y) + +unsigned cfg_long_chain_single_exit(unsigned x) { + unsigned y = 0; +#define BRANCH(i, x, y) if ((x % 13171) < i) { int var = x / 13171; y ^= var; } + EXPAND_4096_BRANCHES(1, x, y); +#undef BRANCH + return y; +} diff --git a/INPUTS/cfg-long-chain2.c b/INPUTS/cfg-long-chain2.c new file mode 100644 index 000000000000..150a084e57b9 --- /dev/null +++ b/INPUTS/cfg-long-chain2.c @@ -0,0 +1,20 @@ +#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y); +#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y) +#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y) +#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y) +#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y) +#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y) +#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y) +#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y) +#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y) +#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y) +#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y) +#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y) + +unsigned cfg_long_chain_multiple_exit(unsigned x) { + unsigned y = 0; +#define BRANCH(i, x, y) if (((x % 13171) + ++y) < i) { int var = x / 13171 + y; return var; } + EXPAND_4096_BRANCHES(1, x, y); +#undef BRANCH + return 42; +} diff --git a/INPUTS/cfg-long-chain3.c b/INPUTS/cfg-long-chain3.c new file mode 100644 index 000000000000..cca4bdcf1ac9 --- /dev/null +++ b/INPUTS/cfg-long-chain3.c @@ -0,0 +1,21 @@ +#define EXPAND_2_BRANCHES(i, x, y) BRANCH(i, x, y); BRANCH(i + 1, x, y); +#define EXPAND_4_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i, x, y) EXPAND_2_BRANCHES(i + 2, x, y) +#define EXPAND_8_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i, x, y) EXPAND_4_BRANCHES(i + 4, x, y) +#define EXPAND_16_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i, x, y) EXPAND_8_BRANCHES(i + 8, x, y) +#define EXPAND_32_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i, x, y) EXPAND_16_BRANCHES(i + 16, x, y) +#define EXPAND_64_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i, x, y) EXPAND_32_BRANCHES(i + 32, x, y) +#define EXPAND_128_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i, x, y) EXPAND_64_BRANCHES(i + 64, x, y) +#define EXPAND_256_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i, x, y) EXPAND_128_BRANCHES(i + 128, x, y) +#define EXPAND_512_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i, x, y) EXPAND_256_BRANCHES(i + 256, x, y) +#define EXPAND_1024_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i, x, y) EXPAND_512_BRANCHES(i + 512, x, y) +#define EXPAND_2048_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i, x, y) EXPAND_1024_BRANCHES(i + 1024, x, y) +#define EXPAND_4096_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i, x, y) EXPAND_2048_BRANCHES(i + 2048, x, y) + +unsigned cfg_long_chain_many_preds(unsigned x) { + unsigned y = 0; +#define BRANCH(i, x, y) if ((x % 13171) < i) { int var = x / 13171; y ^= var; } else + EXPAND_4096_BRANCHES(1, x, y); +#undef BRANCH + int var = x / 13171; y^= var; + return y; +} diff --git a/INPUTS/cfg-nested-switches.c b/INPUTS/cfg-nested-switches.c new file mode 100644 index 000000000000..3db10b443d21 --- /dev/null +++ b/INPUTS/cfg-nested-switches.c @@ -0,0 +1,36 @@ +#define EXPAND_2_INNER_CASES(i, x, y) INNER_CASE(i, x, y); INNER_CASE(i + 1, x, y); +#define EXPAND_4_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i, x, y) EXPAND_2_INNER_CASES(i + 2, x, y) +#define EXPAND_8_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i, x, y) EXPAND_4_INNER_CASES(i + 4, x, y) +#define EXPAND_16_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i, x, y) EXPAND_8_INNER_CASES(i + 8, x, y) +#define EXPAND_32_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i, x, y) EXPAND_16_INNER_CASES(i + 16, x, y) +#define EXPAND_64_INNER_CASES(i, x, y) EXPAND_32_INNER_CASES(i, x, y) EXPAND_32_INNER_CASES(i + 32, x, y) + +#define EXPAND_2_OUTER_CASES(i, x, y) OUTER_CASE(i, x, y); OUTER_CASE(i + 1, x, y); +#define EXPAND_4_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i, x, y) EXPAND_2_OUTER_CASES(i + 2, x, y) +#define EXPAND_8_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i, x, y) EXPAND_4_OUTER_CASES(i + 4, x, y) +#define EXPAND_16_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i, x, y) EXPAND_8_OUTER_CASES(i + 8, x, y) +#define EXPAND_32_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i, x, y) EXPAND_16_OUTER_CASES(i + 16, x, y) +#define EXPAND_64_OUTER_CASES(i, x, y) EXPAND_32_OUTER_CASES(i, x, y) EXPAND_32_OUTER_CASES(i + 32, x, y) + +// Rather than a single monstrous fan-out, this fans out in smaller increments, +// but to a similar size. +unsigned cfg_nested_switch(int x) { + unsigned y = 0; + while (x > 0) { + switch (x) { +#define INNER_CASE(i, x, y) \ + case i: { int case_var = 3*x + i; y += case_var - 1; break; } +#define OUTER_CASE(i, x, y) \ + case i: { \ + int case_var = y >> 8; \ + switch (case_var) { \ + EXPAND_64_INNER_CASES(0, x, y); \ + } \ + break; \ + } +EXPAND_64_OUTER_CASES(0, x, y); + } + --x; + } + return y; +} diff --git a/LICENSE.TXT b/LICENSE.TXT index a378a5f7fd51..91895eba029d 100644 --- a/LICENSE.TXT +++ b/LICENSE.TXT @@ -4,7 +4,7 @@ LLVM Release License University of Illinois/NCSA Open Source License -Copyright (c) 2007-2010 University of Illinois at Urbana-Champaign. +Copyright (c) 2007-2011 University of Illinois at Urbana-Champaign. All rights reserved. Developed by: diff --git a/bindings/python/clang/cindex.py b/bindings/python/clang/cindex.py index 08ad80234e09..8cadcaa7ad06 100644 --- a/bindings/python/clang/cindex.py +++ b/bindings/python/clang/cindex.py @@ -321,6 +321,10 @@ class CursorKind(object): """Test if this is a statement kind.""" return CursorKind_is_stmt(self) + def is_attribute(self): + """Test if this is an attribute kind.""" + return CursorKind_is_attribute(self) + def is_invalid(self): """Test if this is an invalid kind.""" return CursorKind_is_inv(self) @@ -978,8 +982,9 @@ class TranslationUnit(ClangObject): headers. """ def visitor(fobj, lptr, depth, includes): - loc = lptr.contents - includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) + if depth > 0: + loc = lptr.contents + includes.append(FileInclusion(loc.file, File(fobj), loc, depth)) # Automatically adapt CIndex/ctype pointers to python objects includes = [] @@ -1074,7 +1079,7 @@ class File(ClangObject): @property def name(self): """Return the complete file and path name of the file.""" - return File_name(self) + return _CXString_getCString(File_name(self)) @property def time(self): @@ -1147,6 +1152,10 @@ CursorKind_is_stmt = lib.clang_isStatement CursorKind_is_stmt.argtypes = [CursorKind] CursorKind_is_stmt.restype = bool +CursorKind_is_attribute = lib.clang_isAttribute +CursorKind_is_attribute.argtypes = [CursorKind] +CursorKind_is_attribute.restype = bool + CursorKind_is_inv = lib.clang_isInvalid CursorKind_is_inv.argtypes = [CursorKind] CursorKind_is_inv.restype = bool @@ -1183,6 +1192,11 @@ Cursor_spelling.argtypes = [Cursor] Cursor_spelling.restype = _CXString Cursor_spelling.errcheck = _CXString.from_result +Cursor_displayname = lib.clang_getCursorDisplayName +Cursor_displayname.argtypes = [Cursor] +Cursor_displayname.restype = _CXString +Cursor_displayname.errcheck = _CXString.from_result + Cursor_loc = lib.clang_getCursorLocation Cursor_loc.argtypes = [Cursor] Cursor_loc.restype = SourceLocation @@ -1253,7 +1267,7 @@ TranslationUnit_includes.argtypes = [TranslationUnit, # File Functions File_name = lib.clang_getFileName File_name.argtypes = [File] -File_name.restype = c_char_p +File_name.restype = _CXString File_time = lib.clang_getFileTime File_time.argtypes = [File] diff --git a/bindings/python/tests/cindex/test_cursor_kind.py b/bindings/python/tests/cindex/test_cursor_kind.py index bdfa31855835..d7a1cfad8f94 100644 --- a/bindings/python/tests/cindex/test_cursor_kind.py +++ b/bindings/python/tests/cindex/test_cursor_kind.py @@ -18,10 +18,14 @@ def test_kind_groups(): for k in CursorKind.get_all_kinds(): group = [n for n in ('is_declaration', 'is_reference', 'is_expression', - 'is_statement', 'is_invalid') + 'is_statement', 'is_invalid', 'is_attribute') if getattr(k, n)()] - if k == CursorKind.TRANSLATION_UNIT: + if k in ( CursorKind.TRANSLATION_UNIT, + CursorKind.MACRO_DEFINITION, + CursorKind.MACRO_INSTANTIATION, + CursorKind.INCLUSION_DIRECTIVE, + CursorKind.PREPROCESSING_DIRECTIVE): assert len(group) == 0 else: assert len(group) == 1 diff --git a/bindings/python/tests/cindex/test_translation_unit.py b/bindings/python/tests/cindex/test_translation_unit.py index f130db6aeb06..2e65d9518da0 100644 --- a/bindings/python/tests/cindex/test_translation_unit.py +++ b/bindings/python/tests/cindex/test_translation_unit.py @@ -58,24 +58,27 @@ def test_unsaved_files_2(): spellings = [c.spelling for c in tu.cursor.get_children()] assert spellings[-1] == 'x' +def normpaths_equal(path1, path2): + """ Compares two paths for equality after normalizing them with + os.path.normpath + """ + return os.path.normpath(path1) == os.path.normpath(path2) def test_includes(): def eq(expected, actual): if not actual.is_input_file: - return expected[0] == actual.source.name and \ - expected[1] == actual.include.name + return normpaths_equal(expected[0], actual.source.name) and \ + normpaths_equal(expected[1], actual.include.name) else: - return expected[1] == actual.include.name + return normpaths_equal(expected[1], actual.include.name) src = os.path.join(kInputsDir, 'include.cpp') h1 = os.path.join(kInputsDir, "header1.h") h2 = os.path.join(kInputsDir, "header2.h") h3 = os.path.join(kInputsDir, "header3.h") - inc = [(None, src), (src, h1), (h1, h3), (src, h2), (h2, h3)] + inc = [(src, h1), (h1, h3), (src, h2), (h2, h3)] index = Index.create() tu = index.parse(src) for i in zip(inc, tu.get_includes()): assert eq(i[0], i[1]) - - diff --git a/clang.xcodeproj/project.pbxproj b/clang.xcodeproj/project.pbxproj index 1612d122f516..832e07ab44f2 100644 --- a/clang.xcodeproj/project.pbxproj +++ b/clang.xcodeproj/project.pbxproj @@ -373,6 +373,12 @@ 90FD6D90103C3D80005F5B73 /* TypeXML.def */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TypeXML.def; path = clang/Frontend/TypeXML.def; sourceTree = "<group>"; }; 90FD6D91103C3D80005F5B73 /* Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Utils.h; path = clang/Frontend/Utils.h; sourceTree = "<group>"; }; 90FD6DB5103D977E005F5B73 /* index-test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "index-test.cpp"; path = "tools/index-test/index-test.cpp"; sourceTree = "<group>"; }; + BB20603B131EDDBF003C3343 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; }; + BB20603C131EDDBF003C3343 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; }; + BB206041131EDDDA003C3343 /* ARRMT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ARRMT.h; sourceTree = "<group>"; }; + BB206043131EDE03003C3343 /* arrmt-test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = "arrmt-test.cpp"; sourceTree = "<group>"; }; + BB206044131EDE03003C3343 /* CMakeLists.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = "<group>"; }; + BB206045131EDE03003C3343 /* Makefile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.make; path = Makefile; sourceTree = "<group>"; }; BB5C372812A5057500259F53 /* DumpXML.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DumpXML.cpp; path = /Volumes/Data/llvm/tools/clang/lib/AST/DumpXML.cpp; sourceTree = "<absolute>"; }; BBA5AB141309C2FA000B38F1 /* AdjustedReturnValueChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AdjustedReturnValueChecker.cpp; sourceTree = "<group>"; }; BBA5AB151309C2FA000B38F1 /* AnalyzerStatsChecker.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AnalyzerStatsChecker.cpp; sourceTree = "<group>"; }; @@ -561,7 +567,6 @@ BF9FEE321225E898003A8B71 /* ItaniumCXXABI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ItaniumCXXABI.cpp; path = lib/CodeGen/ItaniumCXXABI.cpp; sourceTree = "<group>"; }; BF9FEE341225E8B1003A8B71 /* MicrosoftCXXABI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = MicrosoftCXXABI.cpp; path = lib/CodeGen/MicrosoftCXXABI.cpp; sourceTree = "<group>"; }; BF9FEE361225E8CF003A8B71 /* README.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = README.txt; path = lib/CodeGen/README.txt; sourceTree = "<group>"; }; - BF9FEE371225E925003A8B71 /* BoostConAction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = BoostConAction.cpp; path = lib/Frontend/BoostConAction.cpp; sourceTree = "<group>"; }; BF9FEE451225EA24003A8B71 /* DelayedDiagnostic.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DelayedDiagnostic.h; path = clang/Sema/DelayedDiagnostic.h; sourceTree = "<group>"; }; BF9FEE511226FE9F003A8B71 /* ParseAST.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ParseAST.cpp; path = lib/Parse/ParseAST.cpp; sourceTree = "<group>"; }; BF9FEE531226FEC1003A8B71 /* RAIIObjectsForParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RAIIObjectsForParser.h; path = lib/Parse/RAIIObjectsForParser.h; sourceTree = "<group>"; }; @@ -777,6 +782,7 @@ 08FB7795FE84155DC02AAC07 /* Libraries */ = { isa = PBXGroup; children = ( + BB20603A131EDDBF003C3343 /* ARRMigrate */, BBA5AB121309C2FA000B38F1 /* StaticAnalyzer */, 57EB5660121B034300ECA335 /* Serialization */, BFE2F67911DA95590007EDC0 /* Rewrite */, @@ -899,7 +905,6 @@ 352246E00F5C6BC000D0D279 /* Frontend */ = { isa = PBXGroup; children = ( - BF9FEE371225E925003A8B71 /* BoostConAction.cpp */, 1AFDD8701161085D00AE030A /* ASTMerge.cpp */, 9012911C1048068D0083456D /* ASTUnit.cpp */, 1A2A54A50FD1DD1C00F4CE45 /* ASTConsumers.cpp */, @@ -1105,6 +1110,38 @@ name = "index-test"; sourceTree = "<group>"; }; + BB20603A131EDDBF003C3343 /* ARRMigrate */ = { + isa = PBXGroup; + children = ( + BB20603B131EDDBF003C3343 /* CMakeLists.txt */, + BB20603C131EDDBF003C3343 /* Makefile */, + BDDF60E91337BF40009F1764 /* Transforms.cpp */, + ); + name = ARRMigrate; + path = lib/ARRMigrate; + sourceTree = "<group>"; + }; + BB206040131EDDDA003C3343 /* ARRMigrate */ = { + isa = PBXGroup; + children = ( + BB206041131EDDDA003C3343 /* ARRMT.h */, + ); + name = ARRMigrate; + path = clang/ARRMigrate; + sourceTree = "<group>"; + }; + BB206042131EDE03003C3343 /* arrmt-test */ = { + isa = PBXGroup; + children = ( + BD8A47E7133D32660066FE40 /* ARRMT.cpp */, + BB206043131EDE03003C3343 /* arrmt-test.cpp */, + BB206044131EDE03003C3343 /* CMakeLists.txt */, + BB206045131EDE03003C3343 /* Makefile */, + ); + name = "arrmt-test"; + path = "tools/arrmt-test"; + sourceTree = "<group>"; + }; BBA5AB121309C2FA000B38F1 /* StaticAnalyzer */ = { isa = PBXGroup; children = ( @@ -1575,6 +1612,7 @@ DED7D72E0A524295003AD0FB /* include */ = { isa = PBXGroup; children = ( + BB206040131EDDDA003C3343 /* ARRMigrate */, DED7D7300A524295003AD0FB /* Basic */, DED7D7390A524295003AD0FB /* Lex */, DE1F21F20A7D84E800FBF588 /* Parse */, @@ -1724,6 +1762,7 @@ DEDFE61F0F7B3AE10035BD10 /* Tools */ = { isa = PBXGroup; children = ( + BB206042131EDE03003C3343 /* arrmt-test */, 90F9EFA8104ABDC400D09A15 /* c-index-test */, 9012911E104812DA0083456D /* CIndex */, 90FD6DB4103D9763005F5B73 /* index-test */, diff --git a/docs/AutomaticReferenceCounting.html b/docs/AutomaticReferenceCounting.html new file mode 100644 index 000000000000..5090fa2b7952 --- /dev/null +++ b/docs/AutomaticReferenceCounting.html @@ -0,0 +1,1857 @@ +<html> +<head> +<title>Objective-C Automatic Reference Counting (ARC)</title> +<link type="text/css" rel="stylesheet" href="../menu.css" /> +<link type="text/css" rel="stylesheet" href="../content.css" /> +<style type="text/css"> +/* Collapse the items in the ToC to the left. */ +div#toc ul { + padding-left: 0 +} + +/* Rationales appear in italic. */ +div.rationale { + font-style: italic +} + +div.rationale em { + font-style: normal +} + +div h1 { font-size: 2em; margin: .67em 0 } +div div h1 { font-size: 1.5em; margin: .75em 0 } +div div div h1 { font-size: 1.17em; margin: .83em 0 } +div div div div h1 { margin: 1.12em 0 } + +span.term { font-style: italic; font-weight: bold } +</style> + +<script lang="javascript"> +/// A little script to recursively build a table of contents. +function buildTOC(div, toc, ancestry) { + var children = div.childNodes; + var len = children.length; + + var childNumber = 0; + + var list = null; + for (var i = 0; i < len; ++i) { + var child = children[i]; + if (child.nodeName != "DIV") continue; + if (child.getAttribute("class") == "rationale") continue; + if (child.id == "toc") continue; + + // Okay, we're actually going to build a list node. + if (list === null) list = document.createElement("ul"); + + var childAncestry = ancestry + ++childNumber + "."; + + var headerNode = child.childNodes[1]; + var title = headerNode.innerHTML; + headerNode.insertBefore(document.createTextNode(childAncestry + " "), + headerNode.firstChild); + + var item = document.createElement("li"); + item.appendChild(document.createTextNode(childAncestry + " ")); + + var anchor = document.createElement("a"); + anchor.href = "#" + child.id; + anchor.innerHTML = title; + item.appendChild(anchor); + + buildTOC(child, item, childAncestry); + + list.appendChild(item); + } + if (list) toc.appendChild(list); +} + +function onLoad() { + var toc = document.getElementById("toc"); + var content = document.getElementById("content"); + buildTOC(content, toc, ""); +} +window.onload = onLoad; + +</script> +</head> +<body> + +<!--#include virtual="../menu.html.incl"--> + +<div id="content"> +<h1>Automatic Reference Counting</h1> + +<div id="toc"> +</div> + +<div id="meta"> +<h1>About this document</h1> + +<div id="meta.purpose"> +<h1>Purpose</h1> + +<p>The first and primary purpose of this document is to serve as a +complete technical specification of Automatic Reference Counting. +Given a core Objective-C compiler and runtime, it should be possible +to write a compiler and runtime which implements these new +semantics.</p> + +<p>The secondary purpose is to act as a rationale for why ARC was +designed in this way. This should remain tightly focused on the +technical design and should not stray into marketing speculation.</p> + +</div> <!-- meta.purpose --> + +<div id="meta.background"> +<h1>Background</h1> + +<p>This document assumes a basic familiarity with C.</p> + +<p><span class="term">Blocks</span> are a C language extension for +creating anonymous functions. Users interact with and transfer block +objects using <span class="term">block pointers</span>, which are +represented like a normal pointer. A block may capture values from +local variables; when this occurs, memory must be dynamically +allocated. The initial allocation is done on the stack, but the +runtime provides a <tt>Block_copy</tt> function which, given a block +pointer, either copies the underlying block object to the heap, +setting its reference count to 1 and returning the new block pointer, +or (if the block object is already on the heap) increases its +reference count by 1. The paired function is <tt>Block_release</tt>, +which decreases the reference count by 1 and destroys the object if +the count reaches zero and is on the heap.</p> + +<p>Objective-C is a set of language extensions, significant enough to +be considered a different language. It is a strict superset of C. +The extensions can also be imposed on C++, producing a language called +Objective-C++. The primary feature is a single-inheritance object +system; we briefly describe the modern dialect.</p> + +<p>Objective-C defines a new type kind, collectively called +the <span class="term">object pointer types</span>. This kind has two +notable builtin members, <tt>id</tt> and <tt>Class</tt>; <tt>id</tt> +is the final supertype of all object pointers. The validity of +conversions between object pointer types is not checked at runtime. +Users may define <span class="term">classes</span>; each class is a +type, and the pointer to that type is an object pointer type. A class +may have a superclass; its pointer type is a subtype of its +superclass's pointer type. A class has a set +of <span class="term">ivars</span>, fields which appear on all +instances of that class. For every class <i>T</i> there's an +associated metaclass; it has no fields, its superclass is the +metaclass of <i>T</i>'s superclass, and its metaclass is a global +class. Every class has a global object whose class is the +class's metaclass; metaclasses have no associated type, so pointers to +this object have type <tt>Class</tt>.</p> + +<p>A class declaration (<tt>@interface</tt>) declares a set +of <span class="term">methods</span>. A method has a return type, a +list of argument types, and a <span class="term">selector</span>: a +name like <tt>foo:bar:baz:</tt>, where the number of colons +corresponds to the number of formal arguments. A method may be an +instance method, in which case it can be invoked on objects of the +class, or a class method, in which case it can be invoked on objects +of the metaclass. A method may be invoked by providing an object +(called the <span class="term">receiver</span>) and a list of formal +arguments interspersed with the selector, like so:</p> + +<pre>[receiver foo: fooArg bar: barArg baz: bazArg]</pre> + +<p>This looks in the dynamic class of the receiver for a method with +this name, then in that class's superclass, etc., until it finds +something it can execute. The receiver <q>expression</q> may also be +the name of a class, in which case the actual receiver is the class +object for that class, or (within method definitions) it may +be <tt>super</tt>, in which case the lookup algorithm starts with the +static superclass instead of the dynamic class. The actual methods +dynamically found in a class are not those declared in the +<tt>@interface</tt>, but those defined in a separate +<tt>@implementation</tt> declaration; however, when compiling a +call, typechecking is done based on the methods declared in the +<tt>@interface</tt>.</p> + +<p>Method declarations may also be grouped into +<span class="term">protocols</span>, which are not inherently +associated with any class, but which classes may claim to follow. +Object pointer types may be qualified with additional protocols that +the object is known to support.</p> + +<p><span class="term">Class extensions</span> are collections of ivars +and methods, designed to allow a class's <tt>@interface</tt> to be +split across multiple files; however, there is still a primary +implementation file which must see the <tt>@interface</tt>s of all +class extensions. +<span class="term">Categories</span> allow methods (but not ivars) to +be declared <i>post hoc</i> on an arbitrary class; the methods in the +category's <tt>@implementation</tt> will be dynamically added to that +class's method tables which the category is loaded at runtime, +replacing those methods in case of a collision.</p> + +<p>In the standard environment, objects are allocated on the heap, and +their lifetime is manually managed using a reference count. This is +done using two instance methods which all classes are expected to +implement: <tt>retain</tt> increases the object's reference count by +1, whereas <tt>release</tt> decreases it by 1 and calls the instance +method <tt>dealloc</tt> if the count reaches 0. To simplify certain +operations, there is also an <span class="term">autorelease +pool</span>, a thread-local list of objects to call <tt>release</tt> +on later; an object can be added to this pool by +calling <tt>autorelease</tt> on it.</p> + +<p>Block pointers may be converted to type <tt>id</tt>; block objects +are laid out in a way that makes them compatible with Objective-C +objects. There is a builtin class that all block objects are +considered to be objects of; this class implements <tt>retain</tt> by +adjusting the reference count, not by calling <tt>Block_copy</tt>.</p> + +</div> <!-- meta.background --> + +</div> <!-- meta --> + +<div id="general"> +<h1>General</h1> + +<p>Automatic Reference Counting implements automatic memory management +for Objective-C objects and blocks, freeing the programmer from the +need explicitly insert retains and releases. It does not provide a +cycle collector; users must explicitly manage lifetime instead.</p> + +<p>ARC may be explicitly enabled with the compiler +flag <tt>-fobjc-arc</tt>. It may also be explicitly disabled with the +compiler flag <tt>-fno-objc-arc</tt>. The last of these two flags +appearing on the compile line <q>wins</q>.</p> + +<p>If ARC is enabled, <tt>__has_feature(objc_arc)</tt> will expand to +1 in the preprocessor. For more information about <tt>__has_feature</tt>, +see the <a href="LanguageExtensions.html#__has_feature_extension">language +extensions</a> document.</p> + +</div> + +<div id="objects"> +<h1>Retainable object pointers</h1> + +<p>This section describes retainable object pointers, their basic +operations, and the restrictions imposed on their use under ARC. Note +in particular that it covers the rules for pointer <em>values</em> +(patterns of bits indicating the location of a pointed-to object), not +pointer +<em>objects</em> (locations in memory which store pointer values). +The rules for objects are covered in the next section.</p> + +<p>A <span class="term">retainable object pointer</span> +(or <q>retainable pointer</q>) is a value of +a <span class="term">retainable object pointer type</span> +(<q>retainable type</q>). There are three kinds of retainable object +pointer types:</p> +<ul> +<li>block pointers (formed by applying the caret (<tt>^</tt>) +declarator sigil to a function type)</li> +<li>Objective-C object pointers (<tt>id</tt>, <tt>Class</tt>, <tt>NSFoo*</tt>, etc.)</li> +<li>typedefs marked with <tt>__attribute__((NSObject))</tt></li> +</ul> + +<p>Other pointer types, such as <tt>int*</tt> and <tt>CFStringRef</tt>, +are not subject to ARC's semantics and restrictions.</p> + +<div class="rationale"> + +<p>Rationale: We are not at liberty to require +all code to be recompiled with ARC; therefore, ARC must interoperate +with Objective-C code which manages retains and releases manually. In +general, there are three requirements in order for a +compiler-supported reference-count system to provide reliable +interoperation:</p> + +<ul> +<li>The type system must reliably identify which objects are to be +managed. An <tt>int*</tt> might be a pointer to a <tt>malloc</tt>'ed +array, or it might be a interior pointer to such an array, or it might +point to some field or local variable. In contrast, values of the +retainable object pointer types are never interior.</li> +<li>The type system must reliably indicate how to +manage objects of a type. This usually means that the type must imply +a procedure for incrementing and decrementing retain counts. +Supporting single-ownership objects requires a lot more explicit +mediation in the language.</li> +<li>There must be reliable conventions for whether and +when <q>ownership</q> is passed between caller and callee, for both +arguments and return values. Objective-C methods follow such a +convention very reliably, at least for system libraries on Mac OS X, +and functions always pass objects at +0. The C-based APIs for Core +Foundation objects, on the other hand, have much more varied transfer +semantics.</li> +</ul> +</div> <!-- rationale --> + +<p>The use of <tt>__attribute__((NSObject))</tt> typedefs is not +recommended. If it's absolutely necessary to use this attribute, be +very explicit about using the typedef, and do not assume that it will +be preserved by language features like <tt>__typeof</tt> and C++ +template argument substitution.</p> + +<div class="rationale"><p>Rationale: any compiler operation which +incidentally strips type <q>sugar</q> from a type will yield a type +without the attribute, which may result in unexpected +behavior.</p></div> + +<div id="objects.retains"> +<h1>Retain count semantics</h1> + +<p>A retainable object pointer is either a <span class="term">null +pointer</span> or a pointer to a valid object. Furthermore, if it has +block pointer type and is not <tt>null</tt> then it must actually be a +pointer to a block object, and if it has <tt>Class</tt> type (possibly +protocol-qualified) then it must actually be a pointer to a class +object. Otherwise ARC does not enforce the Objective-C type system as +long as the implementing methods follow the signature of the static +type. It is undefined behavior if ARC is exposed to an invalid +pointer.</p> + +<p>For ARC's purposes, a valid object is one with <q>well-behaved</q> +retaining operations. Specifically, the object must be laid out such +that the Objective-C message send machinery can successfully send it +the following messages:</p> + +<ul> +<li><tt>retain</tt>, taking no arguments and returning a pointer to +the object.</li> +<li><tt>release</tt>, taking no arguments and returning <tt>void</tt>.</li> +<li><tt>autorelease</tt>, taking no arguments and returning a pointer +to the object.</li> +</ul> + +<p>The behavior of these methods is constrained in the following ways. +The term <span class="term">high-level semantics</span> is an +intentionally vague term; the intent is that programmers must +implement these methods in a way such that the compiler, modifying +code in ways it deems safe according to these constraints, will not +violate their requirements. For example, if the user puts logging +statements in <tt>retain</tt>, they should not be surprised if those +statements are executed more or less often depending on optimization +settings. These constraints are not exhaustive of the optimization +opportunities: values held in local variables are subject to +additional restrictions, described later in this document.</p> + +<p>It is undefined behavior if a computation history featuring a send +of <tt>retain</tt> followed by a send of <tt>release</tt> to the same +object, with no intervening <tt>release</tt> on that object, is not +equivalent under the high-level semantics to a computation +history in which these sends are removed. Note that this implies that +these methods may not raise exceptions.</p> + +<p>It is undefined behavior if a computation history features any use +whatsoever of an object following the completion of a send +of <tt>release</tt> that is not preceded by a send of <tt>retain</tt> +to the same object.</p> + +<p>The behavior of <tt>autorelease</tt> must be equivalent to sending +<tt>release</tt> when one of the autorelease pools currently in scope +is popped. It may not throw an exception.</p> + +<p>When the semantics call for performing one of these operations on a +retainable object pointer, if that pointer is <tt>null</tt> then the +effect is a no-op.</p> + +<p>All of the semantics described in this document are subject to +additional <a href="#optimization">optimization rules</a> which permit +the removal or optimization of operations based on local knowledge of +data flow. The semantics describe the high-level behaviors that the +compiler implements, not an exact sequence of operations that a +program will be compiled into.</p> + +</div> <!-- objects.retains --> + +<div id="objects.operands"> +<h1>Retainable object pointers as operands and arguments</h1> + +<p>In general, ARC does not perform retain or release operations when +simply using a retainable object pointer as an operand within an +expression. This includes:</p> +<ul> +<li>loading a retainable pointer from an object with non-weak +<a href="#ownership">ownership</a>,</li> +<li>passing a retainable pointer as an argument to a function or +method, and</li> +<li>receiving a retainable pointer as the result of a function or +method call.</li> +</ul> + +<div class="rationale"><p>Rationale: while this might seem +uncontroversial, it is actually unsafe when multiple expressions are +evaluated in <q>parallel</q>, as with binary operators and calls, +because (for example) one expression might load from an object while +another writes to it. However, C and C++ already call this undefined +behavior because the evaluations are unsequenced, and ARC simply +exploits that here to avoid needing to retain arguments across a large +number of calls.</p></div> + +<p>The remainder of this section describes exceptions to these rules, +how those exceptions are detected, and what those exceptions imply +semantically.</p> + +<div id="objects.operands.consumed"> +<h1>Consumed parameters</h1> + +<p>A function or method parameter of retainable object pointer type +may be marked as <span class="term">consumed</span>, signifying that +the callee expects to take ownership of a +1 retain count. This is +done by adding the <tt>ns_consumed</tt> attribute to the parameter +declaration, like so:</p> + +<pre>void foo(__attribute((ns_consumed)) id x); +- (void) foo: (id) __attribute((ns_consumed)) x;</pre> + +<p>This attribute is part of the type of the function or method, not +the type of the parameter. It controls only how the argument is +passed and received.</p> + +<p>When passing such an argument, ARC retains the argument prior to +making the call.</p> + +<p>When receiving such an argument, ARC releases the argument at the +end of the function, subject to the usual optimizations for local +values.</p> + +<div class="rationale"><p>Rationale: this formalizes direct transfers +of ownership from a caller to a callee. The most common scenario here +is passing the <tt>self</tt> parameter to <tt>init</tt>, but it is +useful to generalize. Typically, local optimization will remove any +extra retains and releases: on the caller side the retain will be +merged with a +1 source, and on the callee side the release will be +rolled into the initialization of the parameter.</p></div> + +<p>The implicit <tt>self</tt> parameter of a method may be marked as +consumed by adding <tt>__attribute__((ns_consumes_self))</tt> to the +method declaration. Methods in the <tt>init</tt> +<a href="#family">family</a> are treated as if they were implicitly +marked with this attribute.</p> + +<p>It is undefined behavior if an Objective-C message send to a method +with <tt>ns_consumed</tt> parameters (other than self) is made with a +null receiver. It is undefined behavior if the method to which an +Objective-C message send statically resolves to has a different set +of <tt>ns_consumed</tt> parameters than the method it dynamically +resolves to. It is undefined behavior if a block or function call is +made through a static type with a different set of <tt>ns_consumed</tt> +parameters than the implementation of the called block or function.</p> + +<div class="rationale"><p>Rationale: consumed parameters with null +receiver are a guaranteed leak. Mismatches with consumed parameters +will cause over-retains or over-releases, depending on the direction. +The rule about function calls is really just an application of the +existing C/C++ rule about calling functions through an incompatible +function type, but it's useful to state it explicitly.</p></div> + +</div> + +<div id="objects.operands.retained_returns"> +<h1>Retained return values</h1> + +<p>A function or method which returns a retainable object pointer type +may be marked as returning a retained value, signifying that the +caller expects to take ownership of a +1 retain count. This is done +by adding the <tt>ns_returns_retained</tt> attribute to the function or +method declaration, like so:</p> + +<pre>id foo(void) __attribute((ns_returns_retained)); +- (id) foo __attribute((ns_returns_retained));</pre> + +<p>This attribute is part of the type of the function or method.</p> + +<p>When returning from such a function or method, ARC retains the +value at the point of evaluation of the return statement, before +leaving all local scopes.</p> + +<p>When receiving a return result from such a function or method, ARC +releases the value at the end of the full-expression it is contained +within, subject to the usual optimizations for local values.</p> + +<div class="rationale"><p>Rationale: this formalizes direct transfers of +ownership from a callee to a caller. The most common scenario this +models is the retained return from <tt>init</tt>, <tt>alloc</tt>, +<tt>new</tt>, and <tt>copy</tt> methods, but there are other cases in +the frameworks. After optimization there are typically no extra +retains and releases required.</p></div> + +<p>Methods in +the <tt>alloc</tt>, <tt>copy</tt>, <tt>init</tt>, <tt>mutableCopy</tt>, +and <tt>new</tt> <a href="#family">families</a> are implicitly marked +<tt>__attribute__((ns_returns_retained))</tt>. This may be suppressed +by explicitly marking the +method <tt>__attribute__((ns_returns_not_retained))</tt>.</p> +</div> + +<p>It is undefined behavior if the method to which an Objective-C +message send statically resolves has different retain semantics on its +result from the method it dynamically resolves to. It is undefined +behavior if a block or function call is made through a static type +with different retain semantics on its result from the implementation +of the called block or function.</p> + +<div class="rationale"><p>Rationale: Mismatches with returned results +will cause over-retains or over-releases, depending on the direction. +Again, the rule about function calls is really just an application of +the existing C/C++ rule about calling functions through an +incompatible function type.</p></div> + + +<div id="objects.operands.other-returns"> +<h1>Unretained return values</h1> + +<p>A method or function which returns a retainable object type but +does not return a retained value must ensure that the object is +still valid across the return boundary.</p> + +<p>When returning from such a function or method, ARC retains the +value at the point of evaluation of the return statement, then leaves +all local scopes, and then balances out the retain while ensuring that +the value lives across the call boundary. In the worst case, this may +involve an <tt>autorelease</tt>, but callers must not assume that the +value is actually in the autorelease pool.</p> + +<p>ARC performs no extra mandatory work on the caller side, although +it may elect to do something to shorten the lifetime of the returned +value.</p> + +<div class="rationale"><p>Rationale: it is common in non-ARC code to not +return an autoreleased value; therefore the convention does not force +either path. It is convenient to not be required to do unnecessary +retains and autoreleases; this permits optimizations such as eliding +retain/autoreleases when it can be shown that the original pointer +will still be valid at the point of return.</p></div> + +<p>A method or function may be marked +with <tt>__attribute__((ns_returns_autoreleased))</tt> to indicate +that it returns a pointer which is guaranteed to be valid at least as +long as the innermost autorelease pool. There are no additional +semantics enforced in the definition of such a method; it merely +enables optimizations in callers.</p> +</div> + +<div id="objects.operands.casts"> +<h1>Bridged casts</h1> + +<p>A <span class="term">bridged cast</span> is a C-style cast +annotated with one of three keywords:</p> + +<ul> +<li><tt>(__bridge T) op</tt> casts the operand to the destination +type <tt>T</tt>. If <tt>T</tt> is a retainable object pointer type, +then <tt>op</tt> must have a non-retainable pointer type. +If <tt>T</tt> is a non-retainable pointer type, then <tt>op</tt> must +have a retainable object pointer type. Otherwise the cast is +ill-formed. There is no transfer of ownership, and ARC inserts +no retain operations.</li> + +<li><tt>(__bridge_retained T) op</tt> casts the operand, which must +have retainable object pointer type, to the destination type, which +must be a non-retainable pointer type. ARC retains the value, subject +to the usual optimizations on local values, and the recipient is +responsible for balancing that +1.</li> + +<li><tt>(__bridge_transfer T) op</tt> casts the operand, which must +have non-retainable pointer type, to the destination type, which must +be a retainable object pointer type. ARC will release the value at +the end of the enclosing full-expression, subject to the usual +optimizations on local values.</li> +</ul> + +<p>These casts are required in order to transfer objects in and out of +ARC control; see the rationale in the section +on <a href="#objects.restrictions.conversion">conversion of retainable +object pointers</a>.</p> + +<p>Using a <tt>__bridge_retained</tt> or <tt>__bridge_transfer</tt> +cast purely to convince ARC to emit an unbalanced retain or release, +respectively, is poor form.</p> + +</div> + +</div> + +<div id="objects.restrictions"> +<h1>Restrictions</h1> + +<div id="objects.restrictions.conversion"> +<h1>Conversion of retainable object pointers</h1> + +<p>In general, a program which attempts to implicitly or explicitly +convert a value of retainable object pointer type to any +non-retainable type, or vice-versa, is ill-formed. For example, an +Objective-C object pointer shall not be converted to <tt>void*</tt>. +As an exception, cast to <tt>intptr_t</tt> is allowed becuase such +casts are not transferring ownership. The <a href="#objects.operands.casts">bridged +casts</a> may be used to perform these conversions where +necessary.</p> + +<div class="rationale"><p>Rationale: we cannot ensure the correct +management of the lifetime of objects if they may be freely passed +around as unmanaged types. The bridged casts are provided so that the +programmer may explicitly describe whether the cast transfers control +into or out of ARC.</p></div> +</div> + +<p>An unbridged cast to a retainable object pointer type of the return +value of a Objective-C message send which yields a non-retainable +pointer is treated as a <tt>__bridge_transfer</tt> cast +if:</p> + +<ul> +<li>the method has the <tt>cf_returns_retained</tt> attribute, or if +not that,</li> +<li>the method does not have the <tt>cf_returns_not_retained</tt> +attribute and</li> +<li>the method's <a href="#family">selector family</a> would imply +the <tt>ns_returns_retained</tt> attribute on a method which returned +a retainable object pointer type.</li> +</ul> + +<p>Otherwise the cast is treated as a <tt>__bridge</tt> cast.</p> + +</div> + +</div> + +<div id="ownership"> +<h1>Ownership qualification</h1> + +<p>This section describes the behavior of <em>objects</em> of +retainable object pointer type; that is, locations in memory which +store retainable object pointers.</p> + +<p>A type is a <span class="term">retainable object owner type</span> +if it is a retainable object pointer type or an array type whose +element type is a retainable object owner type.</p> + +<p>An <span class="term">ownership qualifier</span> is a type +qualifier which applies only to retainable object owner types. An array type is +ownership-qualified according to its element type, and adding an ownership +qualifier to an array type so qualifies its element type.</p> + +<p>A program is ill-formed if it attempts to apply an ownership qualifier +to a type which is already ownership-qualified, even if it is the same +qualifier. There is a single exception to this rule: an ownership qualifier +may be applied to a substituted template type parameter, which overrides the +ownership qualifier provided by the template argument.</p> + +<p>Except as described under +the <a href="#ownership.inference">inference rules</a>, a program is +ill-formed if it attempts to form a pointer or reference type to a +retainable object owner type which lacks an ownership qualifier.</p> + +<div class="rationale"><p>Rationale: these rules, together with the +inference rules, ensure that all objects and lvalues of retainable +object pointer type have an ownership qualifier. The ability to override an ownership qualifier during template substitution is required to counteract the <a href="#ownership.inference.template_arguments">inference of <tt>__strong</tt> for template type arguments</a>. </p></div> + +<p>There are four ownership qualifiers:</p> + +<ul> +<li><tt>__autoreleasing</tt></li> +<li><tt>__strong</tt></li> +<li><tt>__unsafe_unretained</tt></li> +<li><tt>__weak</tt></li> +</ul> + +<p>A type is <span class="term">nontrivially ownership-qualified</span> +if it is qualified with <tt>__autoreleasing</tt>, <tt>__strong</tt>, or +<tt>__weak</tt>.</p> + +<div id="ownership.spelling"> +<h1>Spelling</h1> + +<p>The names of the ownership qualifiers are reserved for the +implementation. A program may not assume that they are or are not +implemented with macros, or what those macros expand to.</p> + +<p>An ownership qualifier may be written anywhere that any other type +qualifier may be written.</p> + +<p>If an ownership qualifier appears in +the <i>declaration-specifiers</i>, the following rules apply:</p> + +<ul> +<li>if the type specifier is a retainable object owner type, the +qualifier applies to that type;</li> +<li>if the outermost non-array part of the declarator is a pointer or +block pointer, the qualifier applies to that type;</li> +<li>otherwise the program is ill-formed.</li> +</ul> + +<p>If an ownership qualifier appears on the declarator name, or on the +declared object, it is applied to outermost pointer or block-pointer +type.</p> + +<p>If an ownership qualifier appears anywhere else in a declarator, it +applies to the type there.</p> + +<div id="ownership.spelling.property"> +<h1>Property declarations</h1> + +<p>A property of retainable object pointer type may have ownership. +If the property's type is ownership-qualified, then the property has +that ownership. If the property has one of the following modifiers, +then the property has the corresponding ownership. A property is +ill-formed if it has conflicting sources of ownership, or if it has +redundant ownership modifiers, or if it has <tt>__autoreleasing</tt> +ownership.</p> + +<ul> +<li><tt>assign</tt> implies <tt>__unsafe_unretained</tt> ownership.</li> +<li><tt>copy</tt> implies <tt>__strong</tt> ownership, as well as the + usual behavior of copy semantics on the setter.</li> +<li><tt>retain</tt> implies <tt>__strong</tt> ownership.</li> +<li><tt>strong</tt> implies <tt>__strong</tt> ownership.</li> +<li><tt>unsafe_unretained</tt> implies <tt>__unsafe_unretained</tt> + ownership.</li> +<li><tt>weak</tt> implies <tt>__weak</tt> ownership.</li> +</ul> + +<p>With the exception of <tt>weak</tt>, these modifiers are available +in non-ARC modes.</p> + +<p>A property's specified ownership is preserved in its metadata, but +otherwise the meaning is purely conventional unless the property is +synthesized. If a property is synthesized, then the +<span class="term">associated instance variable</span> is the +instance variable which is named, possibly implicitly, by the +<tt>@synthesize</tt> declaration. If the associated instance variable +already exists, then its ownership qualification must equal the +ownership of the property; otherwise, the instance variable is created +with that ownership qualification.</p> + +</div> <!-- ownership.spelling.property --> + +</div> <!-- ownership.spelling --> + +<div id="ownership.semantics"> +<h1>Semantics</h1> + +<p>There are five <span class="term">managed operations</span> which +may be performed on an object of retainable object pointer type. Each +qualifier specifies different semantics for each of these operations. +It is still undefined behavior to access an object outside of its +lifetime.</p> + +<p>A load or store with <q>primitive semantics</q> has the same +semantics as the respective operation would have on an <tt>void*</tt> +lvalue with the same alignment and non-ownership qualification.</p> + +<p><span class="term">Reading</span> occurs when performing a +lvalue-to-rvalue conversion on an object lvalue. + +<ul> +<li>For <tt>__weak</tt> objects, the current pointee is retained and +then released at the end of the current full-expression. This must +execute atomically with respect to assignments and to the final +release of the pointee.</li> +<li>For all other objects, the lvalue is loaded with primitive +semantics.</li> +</ul> +</p> + +<p><span class="term">Assignment</span> occurs when evaluating +an assignment operator. The semantics vary based on the qualification: +<ul> +<li>For <tt>__strong</tt> objects, the new pointee is first retained; +second, the lvalue is loaded with primitive semantics; third, the new +pointee is stored into the lvalue with primitive semantics; and +finally, the old pointee is released. This is not performed +atomically; external synchronization must be used to make this safe in +the face of concurrent loads and stores.</li> +<li>For <tt>__weak</tt> objects, the lvalue is updated to point to the +new pointee, unless that object is currently undergoing deallocation, +in which case it the lvalue is updated to a null pointer. This must +execute atomically with respect to other assignments to the object, to +reads from the object, and to the final release of the new pointed-to +value.</li> +<li>For <tt>__unsafe_unretained</tt> objects, the new pointee is +stored into the lvalue using primitive semantics.</li> +<li>For <tt>__autoreleasing</tt> objects, the new pointee is retained, +autoreleased, and stored into the lvalue using primitive semantics.</li> +</ul> +</p> + +<p><span class="term">Initialization</span> occurs when an object's +lifetime begins, which depends on its storage duration. +Initialization proceeds in two stages: +<ol> +<li>First, a null pointer is stored into the lvalue using primitive +semantics. This step is skipped if the object +is <tt>__unsafe_unretained</tt>.</li> +<li>Second, if the object has an initializer, that expression is +evaluated and then assigned into the object using the usual assignment +semantics.</li> +</ol> +</p> + +<p><span class="term">Destruction</span> occurs when an object's +lifetime ends. In all cases it is semantically equivalent to +assigning a null pointer to the object, with the proviso that of +course the object cannot be legally read after the object's lifetime +ends.</p> + +<p><span class="term">Moving</span> occurs in specific situations +where an lvalue is <q>moved from</q>, meaning that its current pointee +will be used but the object may be left in a different (but still +valid) state. This arises with <tt>__block</tt> variables and rvalue +references in C++. For <tt>__strong</tt> lvalues, moving is equivalent +to loading the lvalue with primitive semantics, writing a null pointer +to it with primitive semantics, and then releasing the result of the +load at the end of the current full-expression. For all other +lvalues, moving is equivalent to reading the object.</p> + +</div> <!-- ownership.semantics --> + +<div id="ownership.restrictions"> +<h1>Restrictions</h1> + +<div id="ownership.restrictions.weak"> +<h1>Weak-unavailable types</h1> + +<p>It is explicitly permitted for Objective-C classes to not +support <tt>__weak</tt> references. It is undefined behavior to +perform an operation with weak assignment semantics with a pointer to +an Objective-C object whose class does not support <tt>__weak</tt> +references.</p> + +<div class="rationale"><p>Rationale: historically, it has been +possible for a class to provide its own reference-count implementation +by overriding <tt>retain</tt>, <tt>release</tt>, etc. However, weak +references to an object require coordination with its class's +reference-count implementation because, among other things, weak loads +and stores must be atomic with respect to the final release. +Therefore, existing custom reference-count implementations will +generally not support weak references without additional effort. This +is unavoidable without breaking binary compatibility.</p></div> + +<p>A class may indicate that it does not support weak references by +providing the <tt>objc_arc_weak_unavailable</tt> attribute on the +class's interface declaration. A retainable object pointer type +is <span class="term">weak-unavailable</span> if is a pointer to an +(optionally protocol-qualified) Objective-C class <tt>T</tt> +where <tt>T</tt> or one of its superclasses has +the <tt>objc_arc_weak_unavailable</tt> attribute. A program is +ill-formed if it applies the <tt>__weak</tt> ownership qualifier to a +weak-unavailable type or if the value operand of a weak assignment +operation has a weak-unavailable type.</p> +</div> <!-- ownership.restrictions.weak --> + +<div id="ownership.restrictions.autoreleasing"> +<h1>Storage duration of <tt>__autoreleasing</tt> objects</h1> + +<p>A program is ill-formed if it declares an <tt>__autoreleasing</tt> +object of non-automatic storage duration.</p> + +<div class="rationale"><p>Rationale: autorelease pools are tied to the +current thread and scope by their nature. While it is possible to +have temporary objects whose instance variables are filled with +autoreleased objects, there is no way that ARC can provide any sort of +safety guarantee there.</p></div> + +<p>It is undefined behavior if a non-null pointer is assigned to +an <tt>__autoreleasing</tt> object while an autorelease pool is in +scope and then that object is read after the autorelease pool's scope +is left.</p> + +</div> + +<div id="ownership.restrictions.conversion.indirect"> +<h1>Conversion of pointers to ownership-qualified types</h1> + +<p>A program is ill-formed if an expression of type <tt>T*</tt> is +converted, explicitly or implicitly, to the type <tt>U*</tt>, +where <tt>T</tt> and <tt>U</tt> have different ownership +qualification, unless: +<ul> +<li><tt>T</tt> is qualified with <tt>__strong</tt>, + <tt>__autoreleasing</tt>, or <tt>__unsafe_unretained</tt>, and + <tt>U</tt> is qualified with both <tt>const</tt> and + <tt>__unsafe_unretained</tt>; or</li> +<li>either <tt>T</tt> or <tt>U</tt> is <tt>cv void</tt>, where +<tt>cv</tt> is an optional sequence of non-ownership qualifiers; or</li> +<li>the conversion is requested with a <tt>reinterpret_cast</tt> in + Objective-C++; or</li> +<li>the conversion is a +well-formed <a href="#ownership.restrictions.pass_by_writeback">pass-by-writeback</a>.</li> +</ul> +</p> + +<p>The analogous rule applies to <tt>T&</tt> and <tt>U&</tt> in +Objective-C++.</p> + +<div class="rationale"><p>Rationale: these rules provide a reasonable +level of type-safety for indirect pointers, as long as the underlying +memory is not deallocated. The conversion to <tt>const +__unsafe_unretained</tt> is permitted because the semantics of reads +are equivalent across all these ownership semantics, and that's a very +useful and common pattern. The interconversion with <tt>void*</tt> is +useful for allocating memory or otherwise escaping the type system, +but use it carefully. <tt>reinterpret_cast</tt> is considered to be +an obvious enough sign of taking responsibility for any +problems.</p></div> + +<p>It is undefined behavior to access an ownership-qualified object +through an lvalue of a differently-qualified type, except that any +non-<tt>__weak</tt> object may be read through +an <tt>__unsafe_unretained</tt> lvalue.</p> + +<p>It is undefined behavior if a managed operation is performed on +a <tt>__strong</tt> or <tt>__weak</tt> object without a guarantee that +it contains a primitive zero bit-pattern, or if the storage for such +an object is freed or reused without the object being first assigned a +null pointer.</p> + +<div class="rationale"><p>Rationale: ARC cannot differentiate between +an assignment operator which is intended to <q>initialize</q> dynamic +memory and one which is intended to potentially replace a value. +Therefore the object's pointer must be valid before letting ARC at it. +Similarly, C and Objective-C do not provide any language hooks for +destroying objects held in dynamic memory, so it is the programmer's +responsibility to avoid leaks (<tt>__strong</tt> objects) and +consistency errors (<tt>__weak</tt> objects).</p> + +<p>These requirements are followed automatically in Objective-C++ when +creating objects of retainable object owner type with <tt>new</tt> +or <tt>new[]</tt> and destroying them with <tt>delete</tt>, +<tt>delete[]</tt>, or a pseudo-destructor expression. Note that +arrays of nontrivially-ownership-qualified type are not ABI compatible +with non-ARC code because the element type is non-POD: such arrays +that are <tt>new[]</tt>'d in ARC translation units cannot +be <tt>delete[]</tt>'d in non-ARC translation units and +vice-versa.</p></div> + +</div> + +<div id="ownership.restrictions.pass_by_writeback"> +<h1>Passing to an out parameter by writeback</h1> + +<p>If the argument passed to a parameter of type +<tt>T __autoreleasing *</tt> has type <tt>U oq *</tt>, +where <tt>oq</tt> is an ownership qualifier, then the argument is a +candidate for <span class="term">pass-by-writeback</span> if:</p> + +<ul> +<li><tt>oq</tt> is <tt>__strong</tt> or <tt>__weak</tt>, and +<li>it would be legal to initialize a <tt>T __strong *</tt> with +a <tt>U __strong *</tt>.</li> +</ul> + +<p>For purposes of overload resolution, an implicit conversion +sequence requiring a pass-by-writeback is always worse than an +implicit conversion sequence not requiring a pass-by-writeback.</p> + +<p>The pass-by-writeback is ill-formed if the argument expression does +not have a legal form:</p> + +<ul> +<li><tt>&var</tt>, where <tt>var</tt> is a scalar variable of +automatic storage duration with retainable object pointer type</li> +<li>a conditional expression where the second and third operands are +both legal forms</li> +<li>a cast whose operand is a legal form</li> +<li>a null pointer constant</li> +</ul> + +<div class="rationale"><p>Rationale: the restriction in the form of +the argument serves two purposes. First, it makes it impossible to +pass the address of an array to the argument, which serves to protect +against an otherwise serious risk of mis-inferring an <q>array</q> +argument as an out-parameter. Second, it makes it much less likely +that the user will see confusing aliasing problems due to the +implementation, below, where their store to the writeback temporary is +not immediately seen in the original argument variable.</p></div> + +<p>A pass-by-writeback is evaluated as follows: +<ol> +<li>The argument is evaluated to yield a pointer <tt>p</tt> of + type <tt>U oq *</tt>.</li> +<li>If <tt>p</tt> is a null pointer, then a null pointer is passed as + the argument, and no further work is required for the pass-by-writeback.</li> +<li>Otherwise, a temporary of type <tt>T __autoreleasing</tt> is + created and initialized to a null pointer.</li> +<li>If the argument is not an Objective-C method parameter marked + <tt>out</tt>, then <tt>*p</tt> is read, and the result is written + into the temporary with primitive semantics.</li> +<li>The address of the temporary is passed as the argument to the + actual call.</li> +<li>After the call completes, the temporary is loaded with primitive + semantics, and that value is assigned into <tt>*p</tt>.</li> +</ol></p> + +<div class="rationale"><p>Rationale: this is all admittedly +convoluted. In an ideal world, we would see that a local variable is +being passed to an out-parameter and retroactively modify its type to +be <tt>__autoreleasing</tt> rather than <tt>__strong</tt>. This would +be remarkably difficult and not always well-founded under the C type +system. However, it was judged unacceptably invasive to require +programmers to write <tt>__autoreleasing</tt> on all the variables +they intend to use for out-parameters. This was the least bad +solution.</p></div> + +</div> + +<div id="ownership.restrictions.records"> +<h1>Ownership-qualified fields of structs and unions</h1> + +<p>A program is ill-formed if it declares a member of a C struct or +union to have a nontrivially ownership-qualified type.</p> + +<div class="rationale"><p>Rationale: the resulting type would be +non-POD in the C++ sense, but C does not give us very good language +tools for managing the lifetime of aggregates, so it is more +convenient to simply forbid them. It is still possible to manage this +with a <tt>void*</tt> or an <tt>__unsafe_unretained</tt> +object.</p></div> + +<p>This restriction does not apply in Objective-C++. However, +nontrivally ownership-qualified types are considered non-POD: in C++0x +terms, they are not trivially default constructible, copy +constructible, move constructible, copy assignable, move assignable, +or destructible. It is a violation of C++ One Definition Rule to use +a class outside of ARC that, under ARC, would have an +ownership-qualified member.</p> + +<div class="rationale"><p>Rationale: unlike in C, we can express all +the necessary ARC semantics for ownership-qualified subobjects as +suboperations of the (default) special member functions for the class. +These functions then become non-trivial. This has the non-obvious +repercussion that the class will have a non-trivial copy constructor +and non-trivial destructor; if it wouldn't outside of ARC, this means +that objects of the type will be passed and returned in an +ABI-incompatible manner.</p></div> + +</div> + +</div> + +<div id="ownership.inference"> +<h1>Ownership inference</h1> + +<div id="ownership.inference.variables"> +<h1>Objects</h1> + +<p>If an object is declared with retainable object owner type, but +without an explicit ownership qualifier, its type is implicitly +adjusted to have <tt>__strong</tt> qualification.</p> + +<p>As a special case, if the object's base type is <tt>Class</tt> +(possibly protocol-qualified), the type is adjusted to +have <tt>__unsafe_unretained</tt> qualification instead.</p> + +</div> + +<div id="ownership.inference.indirect_parameters"> +<h1>Indirect parameters</h1> + +<p>If a function or method parameter has type <tt>T*</tt>, where +<tt>T</tt> is an ownership-unqualified retainable object pointer type, +then:</p> + +<ul> +<li>if <tt>T</tt> is <tt>const</tt>-qualified or <tt>Class</tt>, then +it is implicitly qualified with <tt>__unsafe_unretained</tt>;</li> +<li>otherwise, it is implicitly qualified +with <tt>__autoreleasing</tt>.</li> +</ul> +</p> + +<div class="rationale"><p>Rationale: <tt>__autoreleasing</tt> exists +mostly for this case, the Cocoa convention for out-parameters. Since +a pointer to <tt>const</tt> is obviously not an out-parameter, we +instead use a type more useful for passing arrays. If the user +instead intends to pass in a <em>mutable</em> array, inferring +<tt>__autoreleasing</tt> is the wrong thing to do; this directs some +of the caution in the following rules about writeback.</p></div> + +<p>Such a type written anywhere else would be ill-formed by the +general rule requiring ownership qualifiers.</p> + +<p>This rule does not apply in Objective-C++ if a parameter's type is +dependent in a template pattern and is only <em>instantiated</em> to +a type which would be a pointer to an unqualified retainable object +pointer type. Such code is still ill-formed.</p> + +<div class="rationale"><p>Rationale: the convention is very unlikely +to be intentional in template code.</p></div> + +</div> <!-- ownership.inference.indirect_parameters --> + +<div id="ownership.inference.template_arguments"> +<h1>Template arguments</h1> + +<p>If a template argument for a template type parameter is an +retainable object owner type that does not have an explicit ownership +qualifier, it is adjusted to have <tt>__strong</tt> +qualification. This adjustment occurs regardless of whether the +template argument was deduced or explicitly specified. </p> + +<div class="rationale"><p>Rationale: <tt>__strong</tt> is a useful default for containers (e.g., <tt>std::vector<id></tt>), which would otherwise require explicit qualification. Moreover, unqualified retainable object pointer types are unlikely to be useful within templates, since they generally need to have a qualifier applied to the before being used.</p></div> + +</div> <!-- ownership.inference.template_arguments --> +</div> <!-- ownership.inference --> +</div> <!-- ownership --> + + +<div id="family"> +<h1>Method families</h1> + +<p>An Objective-C method may fall into a <span class="term">method +family</span>, which is a conventional set of behaviors ascribed to it +by the Cocoa conventions.</p> + +<p>A method is in a certain method family if: +<ul> +<li>it has a <tt>objc_method_family</tt> attribute placing it in that + family; or if not that,</li> +<li>it does not have an <tt>objc_method_family</tt> attribute placing + it in a different or no family, and</li> +<li>its selector falls into the corresponding selector family, and</li> +<li>its signature obeys the added restrictions of the method family.</li> +</ul></p> + +<p>A selector is in a certain selector family if, ignoring any leading +underscores, the first component of the selector either consists +entirely of the name of the method family or it begins with that name +followed by a character other than a lowercase letter. For +example, <tt>_perform:with:</tt> and <tt>performWith:</tt> would fall +into the <tt>perform</tt> family (if we recognized one), +but <tt>performing:with</tt> would not.</p> + +<p>The families and their added restrictions are:</p> + +<ul> +<li><tt>alloc</tt> methods must return a retainable object pointer type.</li> +<li><tt>copy</tt> methods must return a retainable object pointer type.</li> +<li><tt>mutableCopy</tt> methods must return a retainable object pointer type.</li> +<li><tt>new</tt> methods must return a retainable object pointer type.</li> +<li><tt>init</tt> methods must be instance methods and must return an +Objective-C pointer type. Additionally, a program is ill-formed if it +declares or contains a call to an <tt>init</tt> method whose return +type is neither <tt>id</tt> nor a pointer to a super-class or +sub-class of the declaring class (if the method was declared on +a class) or the static receiver type of the call (if it was declared +on a protocol).</p> + +<div class="rationale"><p>Rationale: there are a fair number of existing +methods with <tt>init</tt>-like selectors which nonetheless don't +follow the <tt>init</tt> conventions. Typically these are either +accidental naming collisions or helper methods called during +initialization. Because of the peculiar retain/release behavior +of <tt>init</tt> methods, it's very important not to treat these +methods as <tt>init</tt> methods if they aren't meant to be. It was +felt that implicitly defining these methods out of the family based on +the exact relationship between the return type and the declaring class +would be much too subtle and fragile. Therefore we identify a small +number of legitimate-seeming return types and call everything else an +error. This serves the secondary purpose of encouraging programmers +not to accidentally give methods names in the <tt>init</tt> family.</p> + +<p>Note that a method with an <tt>init</tt>-family selector which +returns a non-Objective-C type (e.g. <tt>void</tt>) is perfectly +well-formed; it simply isn't in the <tt>init</tt> family.</p></div> +</li> +</ul> + +<p>A program is ill-formed if a method's declarations, +implementations, and overrides do not all have the same method +family.</p> + +<div id="family.attribute"> +<h1>Explicit method family control</h1> + +<p>A method may be annotated with the <tt>objc_method_family</tt> +attribute to precisely control which method family it belongs to. If +a method in an <tt>@implementation</tt> does not have this attribute, +but there is a method declared in the corresponding <tt>@interface</tt> +that does, then the attribute is copied to the declaration in the +<tt>@implementation</tt>. The attribute is available outside of ARC, +and may be tested for with the preprocessor query +<tt>__has_attribute(objc_method_family)</tt>.</p> + +<p>The attribute is spelled +<tt>__attribute__((objc_method_family(<i>family</i>)))</tt>. +If <i>family</i> is <tt>none</tt>, the method has no family, even if +it would otherwise be considered to have one based on its selector and +type. Otherwise, <i>family</i> must be one +of <tt>alloc</tt>, <tt>copy</tt>, <tt>init</tt>, +<tt>mutableCopy</tt>, or <tt>new</tt>, in which case the method is +considered to belong to the corresponding family regardless of its +selector. It is an error if a method that is explicitly added to a +family in this way does not meet the requirements of the family other +than the selector naming convention.</p> + +<div class="rationale"><p>Rationale: the rules codified in this document +describe the standard conventions of Objective-C. However, as these +conventions have not heretofore been enforced by an unforgiving +mechanical system, they are only imperfectly kept, especially as they +haven't always even been precisely defined. While it is possible to +define low-level ownership semantics with attributes like +<tt>ns_returns_retained</tt>, this attribute allows the user to +communicate semantic intent, which of use both to ARC (which, e.g., +treats calls to <tt>init</tt> specially) and the static analyzer.</p></div> +</div> + +<div id="family.semantics"> +<h1>Semantics of method families</h1> + +<p>A method's membership in a method family may imply non-standard +semantics for its parameters and return type.</p> + +<p>Methods in the <tt>alloc</tt>, <tt>copy</tt>, <tt>mutableCopy</tt>, +and <tt>new</tt> families — that is, methods in all the +currently-defined families except <tt>init</tt> — implicitly +<a href="#objects.operands.retained_returns">return a retained +object</a> as if they were annotated with +the <tt>ns_returns_retained</tt> attribute. This can be overridden by +annotating the method with either of +the <tt>ns_returns_autoreleased</tt> or +<tt>ns_returns_not_retained</tt> attributes.</p> + +<p>Properties also follow same naming rules as methods. This means that +those in the <tt>alloc</tt>, <tt>copy</tt>, <tt>mutableCopy</tt>, +and <tt>new</tt> families provide access to +<a href="#objects.operands.retained_returns">retained objects</a>. +This can be overridden by annotating the property with +<tt>ns_returns_not_retained</tt> attribute.</p> + +<div id="family.semantics.init"> +<h1>Semantics of <tt>init</tt></h1> +<p>Methods in the <tt>init</tt> family implicitly +<a href="#objects.operands.consumed">consume</a> their <tt>self</tt> +parameter and <a href="#objects.operands.retained_returns">return a +retained object</a>. Neither of these properties can be altered +through attributes.</p> + +<p>A call to an <tt>init</tt> method with a receiver that is either +<tt>self</tt> (possibly parenthesized or casted) or <tt>super</tt> is +called a <span class="term">delegate init call</span>. It is an error +for a delegate init call to be made except from an <tt>init</tt> +method, and excluding blocks within such methods.</p> + +<p>As an exception to the <a href="misc.self">usual rule</a>, the +variable <tt>self</tt> is mutable in an <tt>init</tt> method and has +the usual semantics for a <tt>__strong</tt> variable. However, it is +undefined behavior and the program is ill-formed, no diagnostic +required, if an <tt>init</tt> method attempts to use the previous +value of <tt>self</tt> after the completion of a delegate init call. +It is conventional, but not required, for an <tt>init</tt> method to +return <tt>self</tt>.</p> + +<p>It is undefined behavior for a program to cause two or more calls +to <tt>init</tt> methods on the same object, except that +each <tt>init</tt> method invocation may perform at most one delegate +init call.</p> + +</div> <!-- family.semantics.init --> + +<div id="family.semantics.result_type"> +<h1>Related result types</h1> + +<p>Certain methods are candidates to have <span class="term">related +result types</span>:</p> +<ul> +<li>class methods in the <tt>alloc</tt> and <tt>new</tt> method families</li> +<li>instance methods in the <tt>init</tt> family</li> +<li>the instance method <tt>self</tt></li> +<li>outside of ARC, the instance methods <tt>retain</tt> and <tt>autorelease</tt></li> +</ul> + +<p>If the formal result type of such a method is <tt>id</tt> or +protocol-qualified <tt>id</tt>, or a type equal to the declaring class +or a superclass, then it is said to have a related result type. In +this case, when invoked in an explicit message send, it is assumed to +return a type related to the type of the receiver:</p> + +<ul> +<li>if it is a class method, and the receiver is a class +name <tt>T</tt>, the message send expression has type <tt>T*</tt>; +otherwise</li> +<li>if it is an instance method, and the receiver has type <tt>T</tt>, +the message send expression has type <tt>T</tt>; otherwise</li> +<li>the message send expression has the normal result type of the +method.</li> +</ul> + +<p>This is a new rule of the Objective-C language and applies outside +of ARC.</p> + +<div class="rationale"><p>Rationale: ARC's automatic code emission is +more prone than most code to signature errors, i.e. errors where a +call was emitted against one method signature, but the implementing +method has an incompatible signature. Having more precise type +information helps drastically lower this risks, as well as catching +a number of latent bugs.</p></div> + +</div> <!-- family.semantics.result_type --> +</div> <!-- family.semantics --> +</div> <!-- family --> + +<div id="optimization"> +<h1>Optimization</h1> + +<p>ARC applies aggressive rules for the optimization of local +behavior. These rules are based around a core assumption of +<span class="term">local balancing</span>: that other code will +perform retains and releases as necessary (and only as necessary) for +its own safety, and so the optimizer does not need to consider global +properties of the retain and release sequence. For example, if a +retain and release immediately bracket a call, the optimizer can +delete the retain and release on the assumption that the called +function will not do a constant number of unmotivated releases +followed by a constant number of <q>balancing</q> retains, such that +the local retain/release pair is the only thing preventing the called +function from ending up with a dangling reference.</p> + +<p>The optimizer assumes that when a new value enters local control, +e.g. from a load of a non-local object or as the result of a function +call, it is instaneously valid. Subsequently, a retain and release of +a value are necessary on a computation path only if there is a use of +that value before the release and after any operation which might +cause a release of the value (including indirectly or non-locally), +and only if the value is not demonstrably already retained.</p> + +<p>The complete optimization rules are quite complicated, but it would +still be useful to document them here.</p> + +</div> + +<div id="misc"> +<h1>Miscellaneous</h1> + +<div id="misc.special_methods"> +<h1>Special methods</h1> + +<div id="misc.special_methods.retain"> +<h1>Memory management methods</h1> + +<p>A program is ill-formed if it contains a method definition, message +send, or <tt>@selector</tt> expression for any of the following +selectors: +<ul> +<li><tt>autorelease</tt></li> +<li><tt>release</tt></li> +<li><tt>retain</tt></li> +<li><tt>retainCount</tt></li> +</ul> +</p> + +<div class="rationale"><p>Rationale: <tt>retainCount</tt> is banned +because ARC robs it of consistent semantics. The others were banned +after weighing three options for how to deal with message sends:</p> + +<p><b>Honoring</b> them would work out very poorly if a programmer +naively or accidentally tried to incorporate code written for manual +retain/release code into an ARC program. At best, such code would do +twice as much work as necessary; quite frequently, however, ARC and +the explicit code would both try to balance the same retain, leading +to crashes. The cost is losing the ability to perform <q>unrooted</q> +retains, i.e. retains not logically corresponding to a strong +reference in the object graph.</p> + +<p><b>Ignoring</b> them would badly violate user expectations about their +code. While it <em>would</em> make it easier to develop code simultaneously +for ARC and non-ARC, there is very little reason to do so except for +certain library developers. ARC and non-ARC translation units share +an execution model and can seamlessly interoperate. Within a +translation unit, a developer who faithfully maintains their code in +non-ARC mode is suffering all the restrictions of ARC for zero +benefit, while a developer who isn't testing the non-ARC mode is +likely to be unpleasantly surprised if they try to go back to it.</p> + +<p><b>Banning</b> them has the disadvantage of making it very awkward +to migrate existing code to ARC. The best answer to that, given a +number of other changes and restrictions in ARC, is to provide a +specialized tool to assist users in that migration.</p> + +<p>Implementing these methods was banned because they are too integral +to the semantics of ARC; many tricks which worked tolerably under +manual reference counting will misbehave if ARC performs an ephemeral +extra retain or two. If absolutely required, it is still possible to +implement them in non-ARC code, for example in a category; the +implementations must obey the <a href="#objects.retains">semantics</a> +laid out elsewhere in this document.</p> + +</div> +</div> <!-- misc.special_methods.retain --> + +<div id="misc.special_methods.dealloc"> +<h1><tt>dealloc</tt></h1> + +<p>A program is ill-formed if it contains a message send +or <tt>@selector</tt> expression for the selector <tt>dealloc</tt>.</p> + +<div class="rationale"><p>Rationale: there are no legitimate reasons +to call <tt>dealloc</tt> directly.</p></div> + +<p>A class may provide a method definition for an instance method +named <tt>dealloc</tt>. This method will be called after the final +<tt>release</tt> of the object but before it is deallocated or any of +its instance variables are destroyed. The superclass's implementation +of <tt>dealloc</tt> will be called automatically when the method +returns.</p> + +<div class="rationale"><p>Rationale: even though ARC destroys instance +variables automatically, there are still legitimate reasons to write +a <tt>dealloc</tt> method, such as freeing non-retainable resources. +Failing to call <tt>[super dealloc]</tt> in such a method is nearly +always a bug. Sometimes, the object is simply trying to prevent +itself from being destroyed, but <tt>dealloc</tt> is really far too +late for the object to be raising such objections. Somewhat more +legitimately, an object may have been pool-allocated and should not be +deallocated with <tt>free</tt>; for now, this can only be supported +with a <tt>dealloc</tt> implementation outside of ARC. Such an +implementation must be very careful to do all the other work +that <tt>NSObject</tt>'s <tt>dealloc</tt> would, which is outside the +scope of this document to describe.</p></div> + +</div> + +</div> <!-- misc.special_methods --> + +<div id="autoreleasepool"> +<h1><tt>@autoreleasepool</tt></h1> + +<p>To simplify the use of autorelease pools, and to bring them under +the control of the compiler, a new kind of statement is available in +Objective-C. It is written <tt>@autoreleasepool</tt> followed by +a <i>compound-statement</i>, i.e. by a new scope delimited by curly +braces. Upon entry to this block, the current state of the +autorelease pool is captured. When the block is exited normally, +whether by fallthrough or directed control flow (such +as <tt>return</tt> or <tt>break</tt>), the autorelease pool is +restored to the saved state, releasing all the objects in it. When +the block is exited with an exception, the pool is not drained.</p> + +<p><tt>@autoreleasepool</tt> may be used in non-ARC translation units, +with equivalent semantics.</p> + +<p>A program is ill-formed if it refers to the +<tt>NSAutoreleasePool</tt> class.</p> + +<div class="rationale"><p>Rationale: autorelease pools are clearly +important for the compiler to reason about, but it is far too much to +expect the compiler to accurately reason about control dependencies +between two calls. It is also very easy to accidentally forget to +drain an autorelease pool when using the manual API, and this can +significantly inflate the process's high-water-mark. The introduction +of a new scope is unfortunate but basically required for sane +interaction with the rest of the language. Not draining the pool +during an unwind is apparently required by the Objective-C exceptions +implementation.</p></div> + +</div> <!-- autoreleasepool --> + +<div id="misc.self"> +<h1><tt>self</tt></h1> + +<p>The <tt>self</tt> parameter variable of an Objective-C method is +never actually retained by the implementation. It is undefined +behavior, or at least dangerous, to cause an object to be deallocated +during a message send to that object. To make this +safe, <tt>self</tt> is implicitly <tt>const</tt> unless the method is +in the <a href="#family.semantics.init"><tt>init</tt> family</a>.</p> + +<div class="rationale"><p>Rationale: the cost of +retaining <tt>self</tt> in all methods was found to be prohibitive, as +it tends to be live across calls, preventing the optimizer from +proving that the retain and release are unnecessary — for good +reason, as it's quite possible in theory to cause an object to be +deallocated during its execution without this retain and release. +Since it's extremely uncommon to actually do so, even unintentionally, +and since there's no natural way for the programmer to remove this +retain/release pair otherwise (as there is for other parameters by, +say, making the variable <tt>__unsafe_unretained</tt>), we chose to +make this optimizing assumption and shift some amount of risk to the +user.</p></div> + +</div> <!-- misc.self --> + +<div id="misc.enumeration"> +<h1>Fast enumeration iteration variables</h1> + +<p>If a variable is declared in the condition of an Objective-C fast +enumeration loop, and the variable has no explicit ownership +qualifier, then it is qualified with <tt>const __strong</tt> and +objects encountered during the enumeration are not actually +retained.</p> + +<div class="rationale"><p>Rationale: this is an optimization made +possible because fast enumeration loops promise to keep the objects +retained during enumeration, and the collection itself cannot be +synchronously modified. It can be overridden by explicitly qualifying +the variable with <tt>__strong</tt>, which will make the variable +mutable again and cause the loop to retain the objects it +encounters.</div> + +</div> + +<div id="misc.blocks"> +<h1>Blocks</h1> + +<p>The implicit <tt>const</tt> capture variables created when +evaluating a block literal expression have the same ownership +semantics as the local variables they capture. The capture is +performed by reading from the captured variable and initializing the +capture variable with that value; the capture variable is destroyed +when the block literal is, i.e. at the end of the enclosing scope.</p> + +<p>The <a href="#ownership.inference">inference</a> rules apply +equally to <tt>__block</tt> variables, which is a shift in semantics +from non-ARC, where <tt>__block</tt> variables did not implicitly +retain during capture.</p> + +<p><tt>__block</tt> variables of retainable object owner type are +moved off the stack by initializing the heap copy with the result of +moving from the stack copy.</tt></p> + +<p>With the exception of retains done as part of initializing +a <tt>__strong</tt> parameter variable or reading a <tt>__weak</tt> +variable, whenever these semantics call for retaining a value of +block-pointer type, it has the effect of a <tt>Block_copy</tt>. The +optimizer may remove such copies when it sees that the result is +used only as an argument to a call.</p> + +</div> <!-- misc.blocks --> + +<div id="misc.exceptions"> +<h1>Exceptions</h1> + +<p>By default in Objective C, ARC is not exception-safe for normal +releases: +<ul> +<li>It does not end the lifetime of <tt>__strong</tt> variables when +their scopes are abnormally terminated by an exception.</li> +<li>It does not perform releases which would occur at the end of +a full-expression if that full-expression throws an exception.</li> +</ul> + +<p>A program may be compiled with the option +<tt>-fobjc-arc-exceptions</tt> in order to enable these, or with the +option <tt>-fno-objc-arc-exceptions</tt> to explicitly disable them, +with the last such argument <q>winning</q>.</p> + +<div class="rationale"><p>Rationale: the standard Cocoa convention is +that exceptions signal programmer error and are not intended to be +recovered from. Making code exceptions-safe by default would impose +severe runtime and code size penalties on code that typically does not +actually care about exceptions safety. Therefore, ARC-generated code +leaks by default on exceptions, which is just fine if the process is +going to be immediately terminated anyway. Programs which do care +about recovering from exceptions should enable the option.</p></div> + +<p>In Objective-C++, <tt>-fobjc-arc-exceptions</tt> is enabled by +default.</p> + +<div class="rationale"><p>Rationale: C++ already introduces pervasive +exceptions-cleanup code of the sort that ARC introduces. C++ +programmers who have not already disabled exceptions are much more +likely to actual require exception-safety.</p></div> + +<p>ARC does end the lifetimes of <tt>__weak</tt> objects when an +exception terminates their scope unless exceptions are disabled in the +compiler.</p> + +<div class="rationale"><p>Rationale: the consequence of a +local <tt>__weak</tt> object not being destroyed is very likely to be +corruption of the Objective-C runtime, so we want to be safer here. +Of course, potentially massive leaks are about as likely to take down +the process as this corruption is if the program does try to recover +from exceptions.</p></div> + +</div> <!-- misc.exceptions --> + +</div> <!-- misc --> + +<div id="runtime"> +<h1>Runtime support</h1> + +<p>This section describes the interaction between the ARC runtime and +the code generated by the ARC compiler. This is not part of the ARC +language specification; instead, it is effectively a language-specific +ABI supplement, akin to the <q>Itanium</q> generic ABI for C++.</p> + +<p>Ownership qualification does not alter the storage requirements for +objects, except that it is undefined behavior if a <tt>__weak</tt> +object is inadequately aligned for an object of type <tt>id</tt>. The +other qualifiers may be used on explicitly under-aligned memory.</p> + +<p>The runtime tracks <tt>__weak</tt> objects which holds non-null +values. It is undefined behavior to direct modify a <tt>__weak</tt> +object which is being tracked by the runtime except through an +<a href="#runtime.objc_storeWeak"><tt>objc_storeWeak</tt></a>, +<a href="#runtime.objc_destroyWeak"><tt>objc_destroyWeak</tt></a>, +or <a href="#runtime.objc_moveWeak"><tt>objc_moveWeak</tt></a> +call.</p> + +<p>The runtime must provide a number of new entrypoints which the +compiler may emit, which are described in the remainder of this +section.</p> + +<div class="rationale"><p>Rationale: Several of these functions are +semantically equivalent to a message send; we emit calls to C +functions instead because:</p> +<ul> +<li>the machine code to do so is significantly smaller,</li> +<li>it is much easier to recognize the C functions in the ARC optimizer, and</li> +<li>a sufficient sophisticated runtime may be able to avoid the +message send in common cases.</li> +</ul> + +<p>Several other of these functions are <q>fused</q> operations which +can be described entirely in terms of other operations. We use the +fused operations primarily as a code-size optimization, although in +some cases there is also a real potential for avoiding redundant +operations in the runtime.</p> + +</div> + +<div id="runtime.objc_autorelease"> +<h1><tt>id objc_autorelease(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +adds the object to the innermost autorelease pool exactly as if the +object had been sent the <tt>autorelease</tt> message.</p> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_autorelease --> + +<div id="runtime.objc_autoreleasePoolPop"> +<h1><tt>void objc_autoreleasePoolPop(void *pool);</tt></h1> +<p><i>Precondition:</i> <tt>pool</tt> is the result of a previous call to +<a href="runtime.objc_autoreleasePoolPush"><tt>objc_autoreleasePoolPush</tt></a> +on the current thread, where neither <tt>pool</tt> nor any enclosing +pool have previously been popped.</p> +<p>Releases all the objects added to the given autorelease pool and +any autorelease pools it encloses, then sets the current autorelease +pool to the pool directly enclosing <tt>pool</tt>.</p> +</div> <!-- runtime.objc_autoreleasePoolPop --> + +<div id="runtime.objc_autoreleasePoolPush"> +<h1><tt>void *objc_autoreleasePoolPush(void);</tt></h1> +<p>Creates a new autorelease pool that is enclosed by the current +pool, makes that the current pool, and returns an opaque <q>handle</q> +to it.</p> + +<div class="rationale"><p>Rationale: while the interface is described +as an explicit hierarchy of pools, the rules allow the implementation +to just keep a stack of objects, using the stack depth as the opaque +pool handle.</p></div> + +</div> <!-- runtime.objc_autoreleasePoolPush --> + +<div id="runtime.objc_autoreleaseReturnValue"> +<h1><tt>id objc_autoreleaseReturnValue(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +makes a best effort to hand off ownership of a retain count on the +object to a call +to <a href="runtime.objc_retainAutoreleasedReturnValue"><tt>objc_retainAutoreleasedReturnValue</tt></a> +for the same object in an enclosing call frame. If this is not +possible, the object is autoreleased as above.</p> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_autoreleaseReturnValue --> + +<div id="runtime.objc_copyWeak"> +<h1><tt>void objc_copyWeak(id *dest, id *src);</tt></h1> +<p><i>Precondition:</i> <tt>src</tt> is a valid pointer which either +contains a null pointer or has been registered as a <tt>__weak</tt> +object. <tt>dest</tt> is a valid pointer which has not been +registered as a <tt>__weak</tt> object.</p> +<p><tt>dest</tt> is initialized to be equivalent to <tt>src</tt>, +potentially registering it with the runtime. Equivalent to the +following code:</p> +<pre>void objc_copyWeak(id *dest, id *src) { + objc_release(objc_initWeak(dest, objc_loadWeakRetained(src))); +}</pre> +<p>Must be atomic with respect to calls to <tt>objc_storeWeak</tt> +on <tt>src</tt>.</p> +</div> <!-- runtime.objc_copyWeak --> + +<div id="runtime.objc_destroyWeak"> +<h1><tt>void objc_destroyWeak(id *object);</tt></h1> +<p><i>Precondition:</i> <tt>object</tt> is a valid pointer which +either contains a null pointer or has been registered as +a <tt>__weak</tt> object.</p> +<p><tt>object</tt> is unregistered as a weak object, if it ever was. +The current value of <tt>object</tt> is left unspecified; otherwise, +equivalent to the following code:</p> +<pre>void objc_destroyWeak(id *object) { + objc_storeWeak(object, nil); +}</pre> +<p>Does not need to be atomic with respect to calls +to <tt>objc_storeWeak</tt> on <tt>object</tt>.</p> +</div> <!-- runtime.objc_destroyWeak --> + +<div id="runtime.objc_initWeak"> +<h1><tt>id objc_initWeak(id *object, id value);</tt></h1> +<p><i>Precondition:</i> <tt>object</tt> is a valid pointer which has +not been registered as a <tt>__weak</tt> object. <tt>value</tt> is +null or a pointer to a valid object.</p> +<p>If <tt>value</tt> is a null pointer or the object to which it +points has begun deallocation, <tt>object</tt> is zero-initialized. +Otherwise, <tt>object</tt> is registered as a <tt>__weak</tt> object +pointing to <tt>value</tt>. Equivalent to the following code:</p> +<pre>id objc_initWeak(id *object, id value) { + *object = nil; + return objc_storeWeak(object, value); +}</pre> +<p>Returns the value of <tt>object</tt> after the call.</p> +<p>Does not need to be atomic with respect to calls +to <tt>objc_storeWeak</tt> on <tt>object</tt>.</p> +</div> <!-- runtime.objc_initWeak --> + +<div id="runtime.objc_loadWeak"> +<h1><tt>id objc_loadWeak(id *object);</tt></h1> +<p><i>Precondition:</i> <tt>object</tt> is a valid pointer which +either contains a null pointer or has been registered as +a <tt>__weak</tt> object.</p> +<p>If <tt>object</tt> is registered as a <tt>__weak</tt> object, and +the last value stored into <tt>object</tt> has not yet been +deallocated or begun deallocation, retains and autoreleases that value +and returns it. Otherwise returns null. Equivalent to the following +code:</p> +<pre>id objc_loadWeak(id *object) { + return objc_autorelease(objc_loadWeakRetained(object)); +}</pre> +<p>Must be atomic with respect to calls to <tt>objc_storeWeak</tt> +on <tt>object</tt>.</p> +<div class="rationale">Rationale: loading weak references would be +inherently prone to race conditions without the retain.</div> +</div> <!-- runtime.objc_loadWeak --> + +<div id="runtime.objc_loadWeakRetained"> +<h1><tt>id objc_loadWeakRetained(id *object);</tt></h1> +<p><i>Precondition:</i> <tt>object</tt> is a valid pointer which +either contains a null pointer or has been registered as +a <tt>__weak</tt> object.</p> +<p>If <tt>object</tt> is registered as a <tt>__weak</tt> object, and +the last value stored into <tt>object</tt> has not yet been +deallocated or begun deallocation, retains that value and returns it. +Otherwise returns null.</p> +<p>Must be atomic with respect to calls to <tt>objc_storeWeak</tt> +on <tt>object</tt>.</p> +</div> <!-- runtime.objc_loadWeakRetained --> + +<div id="runtime.objc_moveWeak"> +<h1><tt>void objc_moveWeak(id *dest, id *src);</tt></h1> +<p><i>Precondition:</i> <tt>src</tt> is a valid pointer which either +contains a null pointer or has been registered as a <tt>__weak</tt> +object. <tt>dest</tt> is a valid pointer which has not been +registered as a <tt>__weak</tt> object.</p> +<p><tt>dest</tt> is initialized to be equivalent to <tt>src</tt>, +potentially registering it with the runtime. <tt>src</tt> may then be +left in its original state, in which case this call is equivalent +to <a href="#runtime.objc_copyWeak"><tt>objc_copyWeak</tt></a>, or it +may be left as null.</p> +<p>Must be atomic with respect to calls to <tt>objc_storeWeak</tt> +on <tt>src</tt>.</p> +</div> <!-- runtime.objc_moveWeak --> + +<div id="runtime.objc_release"> +<h1><tt>void objc_release(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +performs a release operation exactly as if the object had been sent +the <tt>release</tt> message.</p> +</div> <!-- runtime.objc_release --> + +<div id="runtime.objc_retain"> +<h1><tt>id objc_retain(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +performs a retain operation exactly as if the object had been sent +the <tt>retain</tt> message.</p> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_retain --> + +<div id="runtime.objc_retainAutorelease"> +<h1><tt>id objc_retainAutorelease(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +performs a retain operation followed by an autorelease operation. +Equivalent to the following code:</p> +<pre>id objc_retainAutorelease(id value) { + return objc_autorelease(objc_retain(value)); +}</pre> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_retainAutorelease --> + +<div id="runtime.objc_retainAutoreleaseReturnValue"> +<h1><tt>id objc_retainAutoreleaseReturnValue(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +performs a retain operation followed by the operation described in +<a href="#runtime.objc_autoreleaseReturnValue"><tt>objc_autoreleaseReturnValue</tt></a>. +Equivalent to the following code:</p> +<pre>id objc_retainAutoreleaseReturnValue(id value) { + return objc_autoreleaseReturnValue(objc_retain(value)); +}</pre> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_retainAutoreleaseReturnValue --> + +<div id="runtime.objc_retainAutoreleasedReturnValue"> +<h1><tt>id objc_retainAutoreleasedReturnValue(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, it +attempts to accept a hand off of a retain count from a call to +<a href="#runtime.objc_autoreleaseReturnValue"><tt>objc_autoreleaseReturnValue</tt></a> +on <tt>value</tt> in a recently-called function or something it +calls. If that fails, it performs a retain operation exactly +like <a href="#runtime.objc_retain"><tt>objc_retain</tt></a>.</p> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_retainAutoreleasedReturnValue --> + +<div id="runtime.objc_retainBlock"> +<h1><tt>id objc_retainBlock(id value);</tt></h1> +<p><i>Precondition:</i> <tt>value</tt> is null or a pointer to a +valid block object.</p> +<p>If <tt>value</tt> is null, this call has no effect. Otherwise, if +the block pointed to by <tt>value</tt> is still on the stack, it is +copied to the heap and the address of the copy is returned. Otherwise +a retain operation is performed on the block exactly as if it had been +sent the <tt>retain</tt> message.</p> +</div> <!-- runtime.objc_retainBlock --> + +<div id="runtime.objc_storeStrong"> +<h1><tt>id objc_storeStrong(id *object, id value);</tt></h1> +<p><i>Precondition:</i> <tt>object</tt> is a valid pointer to +a <tt>__strong</tt> object which is adequately aligned for a +pointer. <tt>value</tt> is null or a pointer to a valid object.</p> +<p>Performs the complete sequence for assigning to a <tt>__strong</tt> +object of non-block type. Equivalent to the following code:</p> +<pre>id objc_storeStrong(id *object, id value) { + value = [value retain]; + id oldValue = *object; + *object = value; + [oldValue release]; + return value; +}</pre> +<p>Always returns <tt>value</tt>.</p> +</div> <!-- runtime.objc_storeStrong --> + +<div id="runtime.objc_storeWeak"> +<h1><tt>id objc_storeWeak(id *object, id value);</tt></h1> +<p><i>Precondition:</i> <tt>object</tt> is a valid pointer which +either contains a null pointer or has been registered as +a <tt>__weak</tt> object. <tt>value</tt> is null or a pointer to a +valid object.</p> +<p>If <tt>value</tt> is a null pointer or the object to which it +points has begun deallocation, <tt>object</tt> is assigned null +and unregistered as a <tt>__weak</tt> object. Otherwise, +<tt>object</tt> is registered as a <tt>__weak</tt> object or has its +registration updated to point to <tt>value</tt>.</p> +<p>Returns the value of <tt>object</tt> after the call.</p> +</div> <!-- runtime.objc_storeWeak --> + +</div> <!-- runtime --> +</div> <!-- root --> +</body> +</html> diff --git a/docs/Block-ABI-Apple.txt b/docs/Block-ABI-Apple.txt index 4a97aa9bd65e..661ed50594de 100644 --- a/docs/Block-ABI-Apple.txt +++ b/docs/Block-ABI-Apple.txt @@ -317,7 +317,7 @@ would be rewritten to be: int flags; //refcount; int size; int captured_i; - } i = { NULL, &i, 0, sizeof(struct _block_byref_i), 11 }; + } i = { NULL, &i, 0, sizeof(struct _block_byref_i), 10 }; i.forwarding->captured_i = 11; @@ -476,7 +476,7 @@ struct _block_byref_obj { int size; void (*byref_keep)(struct _block_byref_i *dst, struct _block_byref_i *src); void (*byref_dispose)(struct _block_byref_i *); - int captured_obj; + id captured_obj; }; void _block_byref_obj_keep(struct _block_byref_voidBlock *dst, struct _block_byref_voidBlock *src) { diff --git a/docs/BlockLanguageSpec.txt b/docs/BlockLanguageSpec.txt index a612fd28892b..f7bbda365184 100644 --- a/docs/BlockLanguageSpec.txt +++ b/docs/BlockLanguageSpec.txt @@ -151,7 +151,7 @@ For example, given class Foo with member function fighter(void): ...a Block that used foo would import the variables as const variations: const Foo block_foo = foo; // const copy constructor const Foo &block_fooRef = fooRef; - const Foo *block_fooPtr = fooPtr; + Foo *const block_fooPtr = fooPtr; Stack-local objects are copied into a Block via a copy const constructor. If no such constructor exists, it is considered an error to reference such objects from within the Block compound statements. A destructor is run as control leaves the compound statement that contains the Block literal expression. diff --git a/docs/DriverInternals.html b/docs/DriverInternals.html index 4f5f0ae112f9..380de9909af0 100644 --- a/docs/DriverInternals.html +++ b/docs/DriverInternals.html @@ -405,7 +405,7 @@ to each compilation sequence. For example, the list of used temporary files (which must be removed once compilation is finished) and result files (which should be removed if - compilation files).</p> + compilation fails).</p> <h4 id="int_unified_parsing">Unified Parsing & Pipelining</h4> diff --git a/docs/InternalsManual.html b/docs/InternalsManual.html index 961198938f20..5d97609373b7 100644 --- a/docs/InternalsManual.html +++ b/docs/InternalsManual.html @@ -68,7 +68,6 @@ td { <li><a href="#Constants">Constant Folding in the Clang AST</a></li> </ul> </li> -<li><a href="libIndex.html">The Index Library</a></li> <li><a href="#Howtos">Howto guides</a> <ul> <li><a href="#AddingAttributes">How to add an attribute</a></li> @@ -1213,8 +1212,8 @@ void g(); void g(int); </pre> <p>the <code>DeclContext::lookup</code> operation will return - an <code>OverloadedFunctionDecl</code> that contains both - declarations of "g". Clients that perform semantic analysis on a + a <code>DeclContext::lookup_result</code> that contains a range of iterators + over declarations of "g". Clients that perform semantic analysis on a program that is not concerned with the actual source code will primarily use this semantics-centric view.</p> @@ -1396,8 +1395,8 @@ namespace N { nodes in Snippet #1, each of which is a declaration context that contains a single declaration of "f". However, the semantics-centric view provided by name lookup into the namespace <code>N</code> for - "f" will return an <code>OverloadedFunctionDecl</code> that contains - both declarations of "f".</p> + "f" will return a <code>DeclContext::lookup_result</code> that contains + a range of iterators over declarations of "f".</p> <p><code>DeclContext</code> manages multiply-defined declaration contexts internally. The diff --git a/docs/LanguageExtensions.html b/docs/LanguageExtensions.html index 8f43725d82a2..7ee8f010a32b 100644 --- a/docs/LanguageExtensions.html +++ b/docs/LanguageExtensions.html @@ -1,13 +1,17 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> +<!-- Material used from: HTML 4.01 specs: http://www.w3.org/TR/html401/ --> <html> <head> -<title>Clang Language Extensions</title> -<link type="text/css" rel="stylesheet" href="../menu.css" /> -<link type="text/css" rel="stylesheet" href="../content.css" /> -<style type="text/css"> -td { - vertical-align: top; -} -</style> + <META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> + <title>Clang LanguageExtensions</title> + <link type="text/css" rel="stylesheet" href="../menu.css"> + <link type="text/css" rel="stylesheet" href="../content.css"> + <style type="text/css"> + td { + vertical-align: top; + } + </style> </head> <body> @@ -25,12 +29,12 @@ td { <li><a href="#vectors">Vectors and Extended Vectors</a></li> <li><a href="#deprecated">Messages on <tt>deprecated</tt> and <tt>unavailable</tt> attributes</a></li> <li><a href="#attributes-on-enumerators">Attributes on enumerators</a></li> -<li><a href="#checking_language_features">Checks for Standard Language Features</a></li> +<li><a href="#checking_language_features">Checks for Standard Language Features</a> <ul> <li><a href="#cxx_exceptions">C++ exceptions</a></li> <li><a href="#cxx_rtti">C++ RTTI</a></li> - </ul> -<li><a href="#checking_upcoming_features">Checks for Upcoming Standard Language Features</a></li> + </ul></li> +<li><a href="#checking_upcoming_features">Checks for Upcoming Standard Language Features</a> <ul> <li><a href="#cxx0x">C++0x</a> <ul> @@ -39,6 +43,7 @@ td { <li><a href="#cxx_alias_templates">C++0x alias templates</a></li> <li><a href="#cxx_attributes">C++0x attributes</a></li> <li><a href="#cxx_default_function_template_args">C++0x default template arguments in function templates</a></li> + <li><a href="#cxx_delegating_constructor">C++0x delegating constructors</a></li> <li><a href="#cxx_deleted_functions">C++0x deleted functions</a></li> <li><a href="#cxx_lambdas">C++0x lambdas</a></li> <li><a href="#cxx_nullptr">C++0x nullptr</a></li> @@ -53,18 +58,19 @@ td { <li><a href="#cxx_strong_enums">C++0x strongly-typed enumerations</a></li> <li><a href="#cxx_trailing_return">C++0x trailing return type</a></li> <li><a href="#cxx_noexcept">C++0x noexcept specification</a></li> - </ul> + </ul></li> <li><a href="#c1x">C1X</a> <ul> <li><a href="#c_generic_selections">C1X generic selections</a></li> <li><a href="#c_static_assert">C1X <tt>_Static_assert()</tt></a></li> - </ul> - </ul> + </ul></li> + </ul> </li> <li><a href="#checking_type_traits">Checks for Type Traits</a></li> <li><a href="#blocks">Blocks</a></li> <li><a href="#objc_features">Objective-C Features</a> <ul> <li><a href="#objc_instancetype">Related result types</a></li> + <li><a href="#objc_arc">Automatic reference counting</a></li> </ul> </li> <li><a href="#overloading-in-c">Function Overloading in C</a></li> @@ -104,7 +110,7 @@ code without having to resort to something like autoconf or fragile "compiler version checks".</p> <!-- ======================================================================= --> -<h3 id="__has_builtin">__has_builtin</h3> +<h3><a name="__has_builtin">__has_builtin</a></h3> <!-- ======================================================================= --> <p>This function-like macro takes a single identifier argument that is the name @@ -129,7 +135,7 @@ not. It can be used like this:</p> <!-- ======================================================================= --> -<h3 id="__has_feature_extension">__has_feature and __has_extension</h3> +<h3><a name="__has_feature_extension"> __has_feature and __has_extension</a></h3> <!-- ======================================================================= --> <p>These function-like macros take a single identifier argument that is the @@ -174,7 +180,7 @@ non-standardized features, i.e. features not prefixed <code>c_</code>, <p>The feature tag is described along with the language feature below.</p> <!-- ======================================================================= --> -<h3 id="__has_attribute">__has_attribute</h3> +<h3><a name="__has_attribute">__has_attribute</a></h3> <!-- ======================================================================= --> <p>This function-like macro takes a single identifier argument that is the name @@ -208,7 +214,7 @@ check for the existence of an include file before doing a possibly failing #include directive.</p> <!-- ======================================================================= --> -<h3 id="__has_include">__has_include</h3> +<h3><a name="__has_include">__has_include</a></h3> <!-- ======================================================================= --> <p>This function-like macro takes a single file name string argument that @@ -218,12 +224,12 @@ be found using the include paths, or 0 otherwise:</p> <blockquote> <pre> // Note the two possible file name string formats. -#if __has_include("myinclude.h") && __has_include(<stdint.h>) +#if __has_include("myinclude.h") && __has_include(<stdint.h>) # include "myinclude.h" #endif // To avoid problem with non-clang compilers not having this macro. -#if defined(__has_include) && __has_include("myinclude.h") +#if defined(__has_include) && __has_include("myinclude.h") # include "myinclude.h" #endif </pre> @@ -232,7 +238,7 @@ be found using the include paths, or 0 otherwise:</p> <p>To test for this feature, use #if defined(__has_include).</p> <!-- ======================================================================= --> -<h3 id="__has_include_next">__has_include_next</h3> +<h3><a name="__has_include_next">__has_include_next</a></h3> <!-- ======================================================================= --> <p>This function-like macro takes a single file name string argument that @@ -244,12 +250,12 @@ be found using the include paths, or 0 otherwise:</p> <blockquote> <pre> // Note the two possible file name string formats. -#if __has_include_next("myinclude.h") && __has_include_next(<stdint.h>) +#if __has_include_next("myinclude.h") && __has_include_next(<stdint.h>) # include_next "myinclude.h" #endif // To avoid problem with non-clang compilers not having this macro. -#if defined(__has_include_next) && __has_include_next("myinclude.h") +#if defined(__has_include_next) && __has_include_next("myinclude.h") # include_next "myinclude.h" #endif </pre> @@ -410,115 +416,120 @@ noted.</p> C++0x standard. As a result, all these features are enabled with the <tt>-std=c++0x</tt> option when compiling C++ code.</p> -<h4 id="cxx_decltype">C++0x <tt>decltype()</tt></h3> +<h4 id="cxx_decltype">C++0x <tt>decltype()</tt></h4> <p>Use <tt>__has_feature(cxx_decltype)</tt> or <tt>__has_extension(cxx_decltype)</tt> to determine if support for the <tt>decltype()</tt> specifier is enabled.</p> -<h4 id="cxx_access_control_sfinae">C++0x SFINAE includes access control</h3> +<h4 id="cxx_access_control_sfinae">C++0x SFINAE includes access control</h4> <p>Use <tt>__has_feature(cxx_access_control_sfinae)</tt> or <tt>__has_extension(cxx_access_control_sfinae)</tt> to determine whether access-control errors (e.g., calling a private constructor) are considered to be template argument deduction errors (aka SFINAE errors), per <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1170">C++ DR1170</a>.</p> -<h4 id="cxx_alias_templates">C++0x alias templates</h3> +<h4 id="cxx_alias_templates">C++0x alias templates</h4> <p>Use <tt>__has_feature(cxx_alias_templates)</tt> or <tt>__has_extension(cxx_alias_templates)</tt> to determine if support for C++0x's alias declarations and alias templates is enabled.</p> -<h4 id="cxx_attributes">C++0x attributes</h3> +<h4 id="cxx_attributes">C++0x attributes</h4> <p>Use <tt>__has_feature(cxx_attributes)</tt> or <tt>__has_extension(cxx_attributes)</tt> to determine if support for attribute parsing with C++0x's square bracket notation is enabled.</p> -<h4 id="cxx_default_function_template_args">C++0x default template arguments in function templates</h3> +<h4 id="cxx_default_function_template_args">C++0x default template arguments in function templates</h4> <p>Use <tt>__has_feature(cxx_default_function_template_args)</tt> or <tt>__has_extension(cxx_default_function_template_args)</tt> to determine if support for default template arguments in function templates is enabled.</p> -<h4 id="cxx_deleted_functions">C++0x deleted functions</tt></h3> +<h4 id="cxx_delegating_constructors">C++0x delegating constructors</h4> + +<p>Use <tt>__has_feature(cxx_delegating_constructors)</tt> to determine if +support for delegating constructors is enabled.</p> + +<h4 id="cxx_deleted_functions">C++0x <tt>delete</tt>d functions</h4> <p>Use <tt>__has_feature(cxx_deleted_functions)</tt> or <tt>__has_extension(cxx_deleted_functions)</tt> to determine if support for deleted function definitions (with <tt>= delete</tt>) is enabled.</p> -<h4 id="cxx_lambdas">C++0x lambdas</h3> +<h4 id="cxx_lambdas">C++0x lambdas</h4> <p>Use <tt>__has_feature(cxx_lambdas)</tt> or <tt>__has_extension(cxx_lambdas)</tt> to determine if support for lambdas is enabled. clang does not currently implement this feature.</p> -<h4 id="cxx_nullptr">C++0x <tt>nullptr</tt></h3> +<h4 id="cxx_nullptr">C++0x <tt>nullptr</tt></h4> <p>Use <tt>__has_feature(cxx_nullptr)</tt> or <tt>__has_extension(cxx_nullptr)</tt> to determine if support for <tt>nullptr</tt> is enabled.</p> -<h4 id="cxx_override_control">C++0x <tt>override control</tt></h3> +<h4 id="cxx_override_control">C++0x <tt>override control</tt></h4> <p>Use <tt>__has_feature(cxx_override_control)</tt> or <tt>__has_extension(cxx_override_control)</tt> to determine if support for the override control keywords is enabled.</p> -<h4 id="cxx_reference_qualified_functions">C++0x reference-qualified functions</h3> +<h4 id="cxx_reference_qualified_functions">C++0x reference-qualified functions</h4> <p>Use <tt>__has_feature(cxx_reference_qualified_functions)</tt> or <tt>__has_extension(cxx_reference_qualified_functions)</tt> to determine if support for reference-qualified functions (e.g., member functions with <code>&</code> or <code>&&</code> applied to <code>*this</code>) is enabled.</p> -<h4 id="cxx_range_for">C++0x range-based for loop</tt></h3> +<h4 id="cxx_range_for">C++0x range-based <tt>for</tt> loop</h4> <p>Use <tt>__has_feature(cxx_range_for)</tt> or <tt>__has_extension(cxx_range_for)</tt> to determine if support for the range-based for loop is enabled. </p> -<h4 id="cxx_rvalue_references">C++0x rvalue references</tt></h3> +<h4 id="cxx_rvalue_references">C++0x rvalue references</h4> <p>Use <tt>__has_feature(cxx_rvalue_references)</tt> or <tt>__has_extension(cxx_rvalue_references)</tt> to determine if support for rvalue references is enabled. </p> -<h4 id="cxx_static_assert">C++0x <tt>static_assert()</tt></h3> +<h4 id="cxx_static_assert">C++0x <tt>static_assert()</tt></h4> <p>Use <tt>__has_feature(cxx_static_assert)</tt> or <tt>__has_extension(cxx_static_assert)</tt> to determine if support for compile-time assertions using <tt>static_assert</tt> is enabled.</p> -<h4 id="cxx_auto_type">C++0x type inference</h3> +<h4 id="cxx_auto_type">C++0x type inference</h4> <p>Use <tt>__has_feature(cxx_auto_type)</tt> or <tt>__has_extension(cxx_auto_type)</tt> to determine C++0x type inference is supported using the <tt>auto</tt> specifier. If this is disabled, <tt>auto</tt> will instead be a storage class specifier, as in C or C++98.</p> -<h4 id="cxx_variadic_templates">C++0x variadic templates</h3> +<h4 id="cxx_variadic_templates">C++0x variadic templates</h4> <p>Use <tt>__has_feature(cxx_variadic_templates)</tt> or <tt>__has_extension(cxx_variadic_templates)</tt> to determine if support for variadic templates is enabled.</p> -<h4 id="cxx_inline_namespaces">C++0x inline namespaces</h3> +<h4 id="cxx_inline_namespaces">C++0x inline namespaces</h4> <p>Use <tt>__has_feature(cxx_inline_namespaces)</tt> or <tt>__has_extension(cxx_inline_namespaces)</tt> to determine if support for inline namespaces is enabled.</p> -<h4 id="cxx_trailing_return">C++0x trailing return type</h3> +<h4 id="cxx_trailing_return">C++0x trailing return type</h4> <p>Use <tt>__has_feature(cxx_trailing_return)</tt> or <tt>__has_extension(cxx_trailing_return)</tt> to determine if support for the alternate function declaration syntax with trailing return type is enabled.</p> -<h4 id="cxx_noexcept">C++0x noexcept</h3> +<h4 id="cxx_noexcept">C++0x noexcept</h4> <p>Use <tt>__has_feature(cxx_noexcept)</tt> or <tt>__has_extension(cxx_noexcept)</tt> to determine if support for noexcept exception specifications is enabled.</p> -<h4 id="cxx_strong_enums">C++0x strongly typed enumerations</h3> +<h4 id="cxx_strong_enums">C++0x strongly typed enumerations</h4> <p>Use <tt>__has_feature(cxx_strong_enums)</tt> or <tt>__has_extension(cxx_strong_enums)</tt> to determine if support for @@ -530,7 +541,7 @@ strongly typed, scoped enumerations is enabled.</p> C1X standard. As a result, all these features are enabled with the <tt>-std=c1x</tt> option when compiling C code.</p> -<h4 id="c_generic_selections">C1X generic selections</h2> +<h4 id="c_generic_selections">C1X generic selections</h4> <p>Use <tt>__has_feature(c_generic_selections)</tt> or <tt>__has_extension(c_generic_selections)</tt> to determine if support for @@ -544,7 +555,7 @@ C1X draft standard.</p> appropriate standard, but in C++, which lacks the type compatibility rules used in C, types are considered compatible only if they are equivalent.</p> -<h4 id="c_static_assert">C1X <tt>_Static_assert()</tt></h3> +<h4 id="c_static_assert">C1X <tt>_Static_assert()</tt></h4> <p>Use <tt>__has_feature(c_static_assert)</tt> or <tt>__has_extension(c_static_assert)</tt> to determine if support for @@ -554,7 +565,7 @@ compile-time assertions using <tt>_Static_assert</tt> is enabled.</p> <h2 id="checking_type_traits">Checks for Type Traits</h2> <!-- ======================================================================= --> -<p>Clang supports the <a hef="http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html">GNU C++ type traits</a> and a subset of the <a href="http://msdn.microsoft.com/en-us/library/ms177194(v=VS.100).aspx">Microsoft Visual C++ Type traits</a>. For each supported type trait <code>__X</code>, <code>__has_extension(X)</code> indicates the presence of the type trait. For example: +<p>Clang supports the <a href="http://gcc.gnu.org/onlinedocs/gcc/Type-Traits.html">GNU C++ type traits</a> and a subset of the <a href="http://msdn.microsoft.com/en-us/library/ms177194(v=VS.100).aspx">Microsoft Visual C++ Type traits</a>. For each supported type trait <code>__X</code>, <code>__has_extension(X)</code> indicates the presence of the type trait. For example: <blockquote> <pre> #if __has_extension(is_convertible_to) @@ -640,7 +651,7 @@ to have the type <code>NSArray *</code>. If neither <code>alloc</code> nor <code <p>To determine whether a method has a related result type, the first word in the camel-case selector (e.g., "init" in "initWithObjects") is considered, and the method will a related result type if its return -type is compatible with the type of its class and if +type is compatible with the type of its class and if</p> <ul> @@ -650,7 +661,7 @@ type is compatible with the type of its class and if <li>the first word is "autorelease", "init", "retain", or "self", and the method is an instance method.</li> -</ul></p> +</ul> <p>If a method with a related result type is overridden by a subclass method, the subclass method must also return a type that is compatible @@ -670,6 +681,12 @@ with a related result type is treated the same way as method without a related result type.</p> <!-- ======================================================================= --> +<h2 id="objc_arc">Automatic reference counting </h2> +<!-- ======================================================================= --> + +<p>Clang provides support for <a href="AutomaticReferenceCounting.html">automated reference counting</a> in Objective-C, which eliminates the need for manual retain/release/autorelease message sends. There are two feature macros associated with automatic reference counting: <code>__has_feature(objc_arc)</code> indicates the availability of automated reference counting in general, while <code>__has_feature(objc_arc_weak)</code> indicates that automated reference counting also includes support for <code>__weak</code> pointers to Objective-C objects.</p> + +<!-- ======================================================================= --> <h2 id="overloading-in-c">Function Overloading in C</h2> <!-- ======================================================================= --> @@ -790,7 +807,7 @@ vector support</a> instead of builtins, in order to reduce the number of builtins that we need to implement.</p> <!-- ======================================================================= --> -<h3 id="__builtin_shufflevector">__builtin_shufflevector</h3> +<h3><a name="__builtin_shufflevector">__builtin_shufflevector</a></h3> <!-- ======================================================================= --> <p><tt>__builtin_shufflevector</tt> is used to express generic vector @@ -842,7 +859,7 @@ the number of indices specified. <p>Query for this feature with __has_builtin(__builtin_shufflevector).</p> <!-- ======================================================================= --> -<h3 id="__builtin_unreachable">__builtin_unreachable</h3> +<h3><a name="__builtin_unreachable">__builtin_unreachable</a></h3> <!-- ======================================================================= --> <p><tt>__builtin_unreachable</tt> is used to indicate that a specific point in @@ -880,7 +897,7 @@ no arguments and produces a void result. <p>Query for this feature with __has_builtin(__builtin_unreachable).</p> <!-- ======================================================================= --> -<h3 id="__sync_swap">__sync_swap</h3> +<h3><a name="__sync_swap">__sync_swap</a></h3> <!-- ======================================================================= --> <p><tt>__sync_swap</tt> is used to atomically swap integers or pointers in @@ -896,7 +913,7 @@ memory. <p><b>Example of Use:</b></p> <pre> -int old_value = __sync_swap(&value, new_value); +int old_value = __sync_swap(&value, new_value); </pre> <p><b>Description:</b></p> diff --git a/docs/UsersManual.html b/docs/UsersManual.html index 4962a9211066..639892714ef8 100644 --- a/docs/UsersManual.html +++ b/docs/UsersManual.html @@ -846,6 +846,10 @@ variants "__asm__" and "__typeof__" are recognized in all modes.</li> <li>The Apple "blocks" extension is recognized by default in gnu* modes on some platforms; it can be enabled in any mode with the "-fblocks" option.</li> +<li>Arrays that are VLA's according to the standard, but which can be constant + folded by the frontend are treated as fixed size arrays. This occurs for + things like "int X[(1, 2)];", which is technically a VLA. c* modes are + strictly compliant and treat these as VLAs.</li> </ul> <p>Differences between *89 and *99 modes:</p> @@ -882,11 +886,6 @@ extensions are not implemented yet:</p> the uses described in the bug, this is likely to be implemented at some point, at least partially.</li> -<li>clang does not support code generation for local variables pinned to -registers (<a href="http://llvm.org/bugs/show_bug.cgi?id=3933">bug 3933</a>). -This is a relatively small feature, so it is likely to be implemented -relatively soon.</li> - <li>clang does not support decimal floating point types (_Decimal32 and friends) or fixed-point types (_Fract and friends); nobody has expressed interest in these features yet, so it's hard to say when they will be @@ -937,16 +936,9 @@ extension appears to be rarely used. Note that clang <em>does</em> support flexible array members (arrays with a zero or unspecified size at the end of a structure).</li> -<li>clang does not support duplicate definitions of a function where one is -inline. This complicates clients of the AST which normally can expect there is -at most one definition for each function. Source code using this feature should -be changed to define the inline and out-of-line definitions in separate -translation units.</li> - <li>clang does not have an equivalent to gcc's "fold"; this means that clang doesn't accept some constructs gcc might accept in contexts where a -constant expression is required, like "x-x" where x is a variable, or calls -to C library functions like strlen.</li> +constant expression is required, like "x-x" where x is a variable.</li> <li>clang does not support multiple alternative constraints in inline asm; this is an extremely obscure feature which would be complicated to implement diff --git a/docs/index.html b/docs/index.html deleted file mode 100644 index d741b68a4160..000000000000 --- a/docs/index.html +++ /dev/null @@ -1,4 +0,0 @@ -<title>'clang' C frontend documentation</title> - -None yet, sorry :( - diff --git a/include/clang-c/Index.h b/include/clang-c/Index.h index cb0b3c1babcb..4852ded7f883 100644 --- a/include/clang-c/Index.h +++ b/include/clang-c/Index.h @@ -833,14 +833,24 @@ enum CXTranslationUnit_Flags { /** * \brief Used to indicate that the "detailed" preprocessing record, - * if requested, should also contain nested macro instantiations. + * if requested, should also contain nested macro expansions. * - * Nested macro instantiations (i.e., macro instantiations that occur - * inside another macro instantiation) can, in some code bases, require + * Nested macro expansions (i.e., macro expansions that occur + * inside another macro expansion) can, in some code bases, require * a large amount of storage to due preprocessor metaprogramming. Moreover, * its fairly rare that this information is useful for libclang clients. */ - CXTranslationUnit_NestedMacroInstantiations = 0x40 + CXTranslationUnit_NestedMacroExpansions = 0x40, + + /** + * \brief Legacy name to indicate that the "detailed" preprocessing record, + * if requested, should contain nested macro expansions. + * + * \see CXTranslationUnit_NestedMacroExpansions for the current name for this + * value, and its semantics. This is just an alias. + */ + CXTranslationUnit_NestedMacroInstantiations = + CXTranslationUnit_NestedMacroExpansions }; /** @@ -933,6 +943,41 @@ enum CXSaveTranslationUnit_Flags { CINDEX_LINKAGE unsigned clang_defaultSaveOptions(CXTranslationUnit TU); /** + * \brief Describes the kind of error that occurred (if any) in a call to + * \c clang_saveTranslationUnit(). + */ +enum CXSaveError { + /** + * \brief Indicates that no error occurred while saving a translation unit. + */ + CXSaveError_None = 0, + + /** + * \brief Indicates that an unknown error occurred while attempting to save + * the file. + * + * This error typically indicates that file I/O failed when attempting to + * write the file. + */ + CXSaveError_Unknown = 1, + + /** + * \brief Indicates that errors during translation prevented this attempt + * to save the translation unit. + * + * Errors that prevent the translation unit from being saved can be + * extracted using \c clang_getNumDiagnostics() and \c clang_getDiagnostic(). + */ + CXSaveError_TranslationErrors = 2, + + /** + * \brief Indicates that the translation unit to be saved was somehow + * invalid (e.g., NULL). + */ + CXSaveError_InvalidTU = 3 +}; + +/** * \brief Saves a translation unit into a serialized representation of * that translation unit on disk. * @@ -951,8 +996,9 @@ CINDEX_LINKAGE unsigned clang_defaultSaveOptions(CXTranslationUnit TU); * is saved. This should be a bitwise OR of the * CXSaveTranslationUnit_XXX flags. * - * \returns Zero if the translation unit was saved successfully, a - * non-zero value otherwise. + * \returns A value that will match one of the enumerators of the CXSaveError + * enumeration. Zero (CXSaveError_None) indicates that the translation unit was + * saved successfully, while a non-zero value indicates that a problem occurred. */ CINDEX_LINKAGE int clang_saveTranslationUnit(CXTranslationUnit TU, const char *FileName, @@ -1385,7 +1431,8 @@ enum CXCursorKind { /* Preprocessing */ CXCursor_PreprocessingDirective = 500, CXCursor_MacroDefinition = 501, - CXCursor_MacroInstantiation = 502, + CXCursor_MacroExpansion = 502, + CXCursor_MacroInstantiation = CXCursor_MacroExpansion, CXCursor_InclusionDirective = 503, CXCursor_FirstPreprocessing = CXCursor_PreprocessingDirective, CXCursor_LastPreprocessing = CXCursor_InclusionDirective @@ -1474,6 +1521,11 @@ CINDEX_LINKAGE unsigned clang_isExpression(enum CXCursorKind); CINDEX_LINKAGE unsigned clang_isStatement(enum CXCursorKind); /** + * \brief Determine whether the given cursor kind represents an attribute. + */ +CINDEX_LINKAGE unsigned clang_isAttribute(enum CXCursorKind); + +/** * \brief Determine whether the given cursor kind represents an invalid * cursor. */ @@ -2830,6 +2882,137 @@ enum CXCodeComplete_Flags { }; /** + * \brief Bits that represent the context under which completion is occurring. + * + * The enumerators in this enumeration may be bitwise-OR'd together if multiple + * contexts are occurring simultaneously. + */ +enum CXCompletionContext { + /** + * \brief The context for completions is unexposed, as only Clang results + * should be included. (This is equivalent to having no context bits set.) + */ + CXCompletionContext_Unexposed = 0, + + /** + * \brief Completions for any possible type should be included in the results. + */ + CXCompletionContext_AnyType = 1 << 0, + + /** + * \brief Completions for any possible value (variables, function calls, etc.) + * should be included in the results. + */ + CXCompletionContext_AnyValue = 1 << 1, + /** + * \brief Completions for values that resolve to an Objective-C object should + * be included in the results. + */ + CXCompletionContext_ObjCObjectValue = 1 << 2, + /** + * \brief Completions for values that resolve to an Objective-C selector + * should be included in the results. + */ + CXCompletionContext_ObjCSelectorValue = 1 << 3, + /** + * \brief Completions for values that resolve to a C++ class type should be + * included in the results. + */ + CXCompletionContext_CXXClassTypeValue = 1 << 4, + + /** + * \brief Completions for fields of the member being accessed using the dot + * operator should be included in the results. + */ + CXCompletionContext_DotMemberAccess = 1 << 5, + /** + * \brief Completions for fields of the member being accessed using the arrow + * operator should be included in the results. + */ + CXCompletionContext_ArrowMemberAccess = 1 << 6, + /** + * \brief Completions for properties of the Objective-C object being accessed + * using the dot operator should be included in the results. + */ + CXCompletionContext_ObjCPropertyAccess = 1 << 7, + + /** + * \brief Completions for enum tags should be included in the results. + */ + CXCompletionContext_EnumTag = 1 << 8, + /** + * \brief Completions for union tags should be included in the results. + */ + CXCompletionContext_UnionTag = 1 << 9, + /** + * \brief Completions for struct tags should be included in the results. + */ + CXCompletionContext_StructTag = 1 << 10, + + /** + * \brief Completions for C++ class names should be included in the results. + */ + CXCompletionContext_ClassTag = 1 << 11, + /** + * \brief Completions for C++ namespaces and namespace aliases should be + * included in the results. + */ + CXCompletionContext_Namespace = 1 << 12, + /** + * \brief Completions for C++ nested name specifiers should be included in + * the results. + */ + CXCompletionContext_NestedNameSpecifier = 1 << 13, + + /** + * \brief Completions for Objective-C interfaces (classes) should be included + * in the results. + */ + CXCompletionContext_ObjCInterface = 1 << 14, + /** + * \brief Completions for Objective-C protocols should be included in + * the results. + */ + CXCompletionContext_ObjCProtocol = 1 << 15, + /** + * \brief Completions for Objective-C categories should be included in + * the results. + */ + CXCompletionContext_ObjCCategory = 1 << 16, + /** + * \brief Completions for Objective-C instance messages should be included + * in the results. + */ + CXCompletionContext_ObjCInstanceMessage = 1 << 17, + /** + * \brief Completions for Objective-C class messages should be included in + * the results. + */ + CXCompletionContext_ObjCClassMessage = 1 << 18, + /** + * \brief Completions for Objective-C selector names should be included in + * the results. + */ + CXCompletionContext_ObjCSelectorName = 1 << 19, + + /** + * \brief Completions for preprocessor macro names should be included in + * the results. + */ + CXCompletionContext_MacroName = 1 << 20, + + /** + * \brief Natural language completions should be included in the results. + */ + CXCompletionContext_NaturalLanguage = 1 << 21, + + /** + * \brief The current context is unknown, so set all contexts. + */ + CXCompletionContext_Unknown = ((1 << 22) - 1) +}; + +/** * \brief Returns a default set of code-completion options that can be * passed to\c clang_codeCompleteAt(). */ @@ -2950,6 +3133,19 @@ CXDiagnostic clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *Results, unsigned Index); /** + * \brief Determines what compeltions are appropriate for the context + * the given code completion. + * + * \param Results the code completion results to query + * + * \returns the kinds of completions that are appropriate for use + * along with the given code completion results. + */ +CINDEX_LINKAGE +unsigned long long clang_codeCompleteGetContexts( + CXCodeCompleteResults *Results); + +/** * @} */ @@ -3005,6 +3201,51 @@ CINDEX_LINKAGE void clang_getInclusions(CXTranslationUnit tu, * @} */ +/** \defgroup CINDEX_REMAPPING Remapping functions + * + * @{ + */ + +/** + * \brief A remapping of original source files and their translated files. + */ +typedef void *CXRemapping; + +/** + * \brief Retrieve a remapping. + * + * \param path the path that contains metadata about remappings. + * + * \returns the requested remapping. This remapping must be freed + * via a call to \c clang_remap_dispose(). Can return NULL if an error occurred. + */ +CINDEX_LINKAGE CXRemapping clang_getRemappings(const char *path); + +/** + * \brief Determine the number of remappings. + */ +CINDEX_LINKAGE unsigned clang_remap_getNumFiles(CXRemapping); + +/** + * \brief Get the original and the associated filename from the remapping. + * + * \param original If non-NULL, will be set to the original filename. + * + * \param transformed If non-NULL, will be set to the filename that the original + * is associated with. + */ +CINDEX_LINKAGE void clang_remap_getFilenames(CXRemapping, unsigned index, + CXString *original, CXString *transformed); + +/** + * \brief Dispose the remapping. + */ +CINDEX_LINKAGE void clang_remap_dispose(CXRemapping); + +/** + * @} + */ + /** * @} */ diff --git a/include/clang/ARCMigrate/ARCMT.h b/include/clang/ARCMigrate/ARCMT.h new file mode 100644 index 000000000000..ad5cf4a2c16c --- /dev/null +++ b/include/clang/ARCMigrate/ARCMT.h @@ -0,0 +1,94 @@ +//===-- ARCMT.h - ARC Migration Rewriter ------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ARCMIGRATE_ARCMT_H +#define LLVM_CLANG_ARCMIGRATE_ARCMT_H + +#include "clang/ARCMigrate/FileRemapper.h" +#include "clang/Frontend/CompilerInvocation.h" + +namespace clang { + class ASTContext; + class DiagnosticClient; + +namespace arcmt { + class MigrationPass; + +/// \brief Creates an AST with the provided CompilerInvocation but with these +/// changes: +/// -if a PCH/PTH is set, the original header is used instead +/// -Automatic Reference Counting mode is enabled +/// +/// It then checks the AST and produces errors/warning for ARC migration issues +/// that the user needs to handle manually. +/// +/// \returns false if no error is produced, true otherwise. +bool checkForManualIssues(CompilerInvocation &CI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient); + +/// \brief Works similar to checkForManualIssues but instead of checking, it +/// applies automatic modifications to source files to conform to ARC. +/// +/// \returns false if no error is produced, true otherwise. +bool applyTransformations(CompilerInvocation &origCI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient); + +/// \brief Applies automatic modifications and produces temporary files +/// and metadata into the \arg outputDir path. +/// +/// \returns false if no error is produced, true otherwise. +bool migrateWithTemporaryFiles(CompilerInvocation &origCI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient, + llvm::StringRef outputDir); + +/// \brief Get the set of file remappings from the \arg outputDir path that +/// migrateWithTemporaryFiles produced. +/// +/// \returns false if no error is produced, true otherwise. +bool getFileRemappings(std::vector<std::pair<std::string,std::string> > &remap, + llvm::StringRef outputDir, + DiagnosticClient *DiagClient); + +typedef void (*TransformFn)(MigrationPass &pass); + +std::vector<TransformFn> getAllTransformations(); + +class MigrationProcess { + CompilerInvocation OrigCI; + DiagnosticClient *DiagClient; + FileRemapper Remapper; + +public: + MigrationProcess(const CompilerInvocation &CI, DiagnosticClient *diagClient, + llvm::StringRef outputDir = llvm::StringRef()); + + class RewriteListener { + public: + virtual ~RewriteListener(); + + virtual void start(ASTContext &Ctx) { } + virtual void finish() { } + + virtual void insert(SourceLocation loc, llvm::StringRef text) { } + virtual void remove(CharSourceRange range) { } + }; + + bool applyTransform(TransformFn trans, RewriteListener *listener = 0); + + FileRemapper &getRemapper() { return Remapper; } +}; + +} // end namespace arcmt + +} // end namespace clang + +#endif diff --git a/include/clang/ARCMigrate/ARCMTActions.h b/include/clang/ARCMigrate/ARCMTActions.h new file mode 100644 index 000000000000..4c714f55b390 --- /dev/null +++ b/include/clang/ARCMigrate/ARCMTActions.h @@ -0,0 +1,47 @@ +//===--- ARCMTActions.h - ARC Migrate Tool Frontend Actions -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ARCMIGRATE_ARCMT_ACTION_H +#define LLVM_CLANG_ARCMIGRATE_ARCMT_ACTION_H + +#include "clang/Frontend/FrontendAction.h" +#include "llvm/ADT/OwningPtr.h" + +namespace clang { +namespace arcmt { + +class CheckAction : public WrapperFrontendAction { +protected: + virtual bool BeginInvocation(CompilerInstance &CI); + +public: + CheckAction(FrontendAction *WrappedAction); +}; + +class ModifyAction : public WrapperFrontendAction { +protected: + virtual bool BeginInvocation(CompilerInstance &CI); + +public: + ModifyAction(FrontendAction *WrappedAction); +}; + +class MigrateAction : public WrapperFrontendAction { + std::string MigrateDir; +protected: + virtual bool BeginInvocation(CompilerInstance &CI); + +public: + MigrateAction(FrontendAction *WrappedAction, llvm::StringRef migrateDir); +}; + +} +} + +#endif diff --git a/include/clang/ARCMigrate/FileRemapper.h b/include/clang/ARCMigrate/FileRemapper.h new file mode 100644 index 000000000000..809f6a5f71be --- /dev/null +++ b/include/clang/ARCMigrate/FileRemapper.h @@ -0,0 +1,76 @@ +//===-- FileRemapper.h - File Remapping Helper ------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ARCMIGRATE_FILEREMAPPER_H +#define LLVM_CLANG_ARCMIGRATE_FILEREMAPPER_H + +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" + +namespace llvm { + class MemoryBuffer; +} + +namespace clang { + class FileManager; + class FileEntry; + class Diagnostic; + class CompilerInvocation; + +namespace arcmt { + +class FileRemapper { + // FIXME: Reuse the same FileManager for multiple ASTContexts. + llvm::OwningPtr<FileManager> FileMgr; + + typedef llvm::PointerUnion<const FileEntry *, llvm::MemoryBuffer *> Target; + typedef llvm::DenseMap<const FileEntry *, Target> MappingsTy; + MappingsTy FromToMappings; + + llvm::DenseMap<const FileEntry *, const FileEntry *> ToFromMappings; + +public: + FileRemapper(); + ~FileRemapper(); + + bool initFromDisk(llvm::StringRef outputDir, Diagnostic &Diag, + bool ignoreIfFilesChanged); + bool flushToDisk(llvm::StringRef outputDir, Diagnostic &Diag); + + bool overwriteOriginal(Diagnostic &Diag, + llvm::StringRef outputDir = llvm::StringRef()); + + void remap(llvm::StringRef filePath, llvm::MemoryBuffer *memBuf); + void remap(llvm::StringRef filePath, llvm::StringRef newPath); + + void applyMappings(CompilerInvocation &CI) const; + + void transferMappingsAndClear(CompilerInvocation &CI); + + void clear(llvm::StringRef outputDir = llvm::StringRef()); + +private: + void remap(const FileEntry *file, llvm::MemoryBuffer *memBuf); + void remap(const FileEntry *file, const FileEntry *newfile); + + const FileEntry *getOriginalFile(llvm::StringRef filePath); + void resetTarget(Target &targ); + + bool report(const std::string &err, Diagnostic &Diag); + + std::string getRemapInfoFile(llvm::StringRef outputDir); +}; + +} // end namespace arcmt + +} // end namespace clang + +#endif diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index 517c25df24bb..1526f36ba2fb 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -124,7 +124,10 @@ class ASTContext : public llvm::RefCountedBase<ASTContext> { mutable llvm::FoldingSet<QualifiedTemplateName> QualifiedTemplateNames; mutable llvm::FoldingSet<DependentTemplateName> DependentTemplateNames; - mutable llvm::FoldingSet<SubstTemplateTemplateParmPackStorage> + mutable llvm::FoldingSet<SubstTemplateTemplateParmStorage> + SubstTemplateTemplateParms; + mutable llvm::ContextualFoldingSet<SubstTemplateTemplateParmPackStorage, + ASTContext&> SubstTemplateTemplateParmPacks; /// \brief The set of nested name specifiers. @@ -993,6 +996,18 @@ public: return getExtQualType(T, Qs); } + /// getLifetimeQualifiedType - Returns a type with the given + /// lifetime qualifier. + QualType getLifetimeQualifiedType(QualType type, + Qualifiers::ObjCLifetime lifetime) { + assert(type.getObjCLifetime() == Qualifiers::OCL_None); + assert(lifetime != Qualifiers::OCL_None); + + Qualifiers qs; + qs.addObjCLifetime(lifetime); + return getQualifiedType(type, qs); + } + DeclarationNameInfo getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const; @@ -1007,6 +1022,8 @@ public: const IdentifierInfo *Name) const; TemplateName getDependentTemplateName(NestedNameSpecifier *NNS, OverloadedOperatorKind Operator) const; + TemplateName getSubstTemplateTemplateParm(TemplateTemplateParmDecl *param, + TemplateName replacement) const; TemplateName getSubstTemplateTemplateParmPack(TemplateTemplateParmDecl *Param, const TemplateArgument &ArgPack) const; @@ -1044,7 +1061,9 @@ public: /// isObjCNSObjectType - Return true if this is an NSObject object with /// its NSObject attribute set. - bool isObjCNSObjectType(QualType Ty) const; + static bool isObjCNSObjectType(QualType Ty) { + return Ty->isObjCNSObjectType(); + } //===--------------------------------------------------------------------===// // Type Sizing and Analysis @@ -1315,6 +1334,18 @@ public: /// getConstantArrayElementCount - Returns number of constant array elements. uint64_t getConstantArrayElementCount(const ConstantArrayType *CA) const; + /// \brief Perform adjustment on the parameter type of a function. + /// + /// This routine adjusts the given parameter type @p T to the actual + /// parameter type used by semantic analysis (C99 6.7.5.3p[7,8], + /// C++ [dcl.fct]p3). The adjusted parameter type is returned. + QualType getAdjustedParameterType(QualType T); + + /// \brief Retrieve the parameter type as adjusted for use in the signature + /// of a function, decaying array and function types and removing top-level + /// cv-qualifiers. + QualType getSignatureParameterType(QualType T); + /// getArrayDecayedType - Return the properly qualified result of decaying the /// specified array type to a pointer. This operation is non-trivial when /// handling typedefs etc. The canonical type of "T" must be an array type, @@ -1328,6 +1359,10 @@ public: /// integer type. QualType getPromotedIntegerType(QualType PromotableType) const; + /// \brief Recurses in pointer/array types until it finds an objc retainable + /// type and returns its ownership. + Qualifiers::ObjCLifetime getInnerObjCOwnership(QualType T) const; + /// \brief Whether this is a promotable bitfield reference according /// to C99 6.3.1.1p2, bullet 2 (and GCC extensions). /// @@ -1382,6 +1417,7 @@ public: bool typesAreCompatible(QualType T1, QualType T2, bool CompareUnqualified = false); // C99 6.2.7p1 + bool propertyTypesAreCompatible(QualType, QualType); bool typesAreBlockPointerCompatible(QualType, QualType); bool isObjCIdType(QualType T) const { diff --git a/include/clang/AST/ASTDiagnostic.h b/include/clang/AST/ASTDiagnostic.h index 1cb803a3396a..70a548d4e965 100644 --- a/include/clang/AST/ASTDiagnostic.h +++ b/include/clang/AST/ASTDiagnostic.h @@ -33,16 +33,18 @@ namespace clang { /// diagnostics. It is meant to be used as the argument to /// \c Diagnostic::SetArgToStringFn(), where the cookie is an \c ASTContext /// pointer. - void FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, - intptr_t Val, - const char *Modifier, - unsigned ModLen, - const char *Argument, - unsigned ArgLen, - const Diagnostic::ArgumentValue *PrevArgs, - unsigned NumPrevArgs, - llvm::SmallVectorImpl<char> &Output, - void *Cookie); + void FormatASTNodeDiagnosticArgument( + Diagnostic::ArgumentKind Kind, + intptr_t Val, + const char *Modifier, + unsigned ModLen, + const char *Argument, + unsigned ArgLen, + const Diagnostic::ArgumentValue *PrevArgs, + unsigned NumPrevArgs, + llvm::SmallVectorImpl<char> &Output, + void *Cookie, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals); } // end namespace clang #endif diff --git a/include/clang/AST/CanonicalType.h b/include/clang/AST/CanonicalType.h index 251881490499..38e6b41977f8 100644 --- a/include/clang/AST/CanonicalType.h +++ b/include/clang/AST/CanonicalType.h @@ -250,7 +250,6 @@ public: LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isObjectType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIncompleteType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIncompleteOrObjectType) - LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isPODType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isVariablyModifiedType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isIntegerType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isEnumeralType) @@ -295,6 +294,7 @@ public: LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isUnsignedIntegerOrEnumerationType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isConstantSizeType) LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(bool, isSpecifierType) + LLVM_CLANG_CANPROXY_SIMPLE_ACCESSOR(CXXRecordDecl*, getAsCXXRecordDecl) /// \brief Retrieve the proxy-adaptor type. /// diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h index d993d345ef96..5691e99e7297 100644 --- a/include/clang/AST/Decl.h +++ b/include/clang/AST/Decl.h @@ -702,8 +702,12 @@ private: /// \brief Whether this variable is the for-range-declaration in a C++0x /// for-range statement. unsigned CXXForRangeDecl : 1; + + /// \brief Whether this variable is an ARC pseudo-__strong + /// variable; see isARCPseudoStrong() for details. + unsigned ARCPseudoStrong : 1; }; - enum { NumVarDeclBits = 13 }; // two reserved bits for now + enum { NumVarDeclBits = 13 }; // one reserved bit friend class ASTDeclReader; friend class StmtIteratorBase; @@ -975,6 +979,20 @@ public: void setInit(Expr *I); + /// \brief Determine whether this variable is a reference that + /// extends the lifetime of its temporary initializer. + /// + /// A reference extends the lifetime of its temporary initializer if + /// it's initializer is an rvalue that would normally go out of scope + /// at the end of the initializer (a full expression). In such cases, + /// the reference itself takes ownership of the temporary, which will + /// be destroyed when the reference goes out of scope. For example: + /// + /// \code + /// const int &r = 1.0; // creates a temporary of type 'int' + /// \endcode + bool extendsLifetimeOfTemporary() const; + EvaluatedStmt *EnsureEvaluatedStmt() const { EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>(); if (!Eval) { @@ -1102,6 +1120,13 @@ public: /// a C++0x for-range statement. bool isCXXForRangeDecl() const { return VarDeclBits.CXXForRangeDecl; } void setCXXForRangeDecl(bool FRD) { VarDeclBits.CXXForRangeDecl = FRD; } + + /// \brief Determine whether this variable is an ARC pseudo-__strong + /// variable. A pseudo-__strong variable has a __strong-qualified + /// type but does not actually retain the object written into it. + /// Generally such variables are also 'const' for safety. + bool isARCPseudoStrong() const { return VarDeclBits.ARCPseudoStrong; } + void setARCPseudoStrong(bool ps) { VarDeclBits.ARCPseudoStrong = ps; } /// \brief If this variable is an instantiated static data member of a /// class template specialization, returns the templated static data member @@ -2014,6 +2039,11 @@ public: InitializerOrBitWidth.setPointer(BW); InitializerOrBitWidth.setInt(1); } + /// removeBitWidth - Remove the bitfield width from this member. + void removeBitWidth() { + assert(isBitField() && "no bit width to remove"); + InitializerOrBitWidth.setPointer(0); + } /// hasInClassInitializer - Determine whether this member has a C++0x in-class /// initializer. @@ -2956,6 +2986,8 @@ public: bool capturesCXXThis() const { return CapturesCXXThis; } + bool capturesVariable(const VarDecl *var) const; + void setCaptures(ASTContext &Context, const Capture *begin, const Capture *end, diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index b5047b91c0e8..8b2ef2a81acb 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -381,7 +381,24 @@ public: attr_iterator attr_end() const { return hasAttrs() ? getAttrs().end() : 0; } - + + template <typename T> + void dropAttr() { + if (!HasAttrs) return; + + AttrVec &Attrs = getAttrs(); + for (unsigned i = 0, e = Attrs.size(); i != e; /* in loop */) { + if (isa<T>(Attrs[i])) { + Attrs.erase(Attrs.begin() + i); + --e; + } + else + ++i; + } + if (Attrs.empty()) + HasAttrs = false; + } + template <typename T> specific_attr_iterator<T> specific_attr_begin() const { return specific_attr_iterator<T>(attr_begin()); diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index 42a12eb5a34c..dd490f48c52c 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -19,7 +19,6 @@ #include "clang/AST/Decl.h" #include "clang/AST/TypeLoc.h" #include "clang/AST/UnresolvedSet.h" -#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallPtrSet.h" namespace clang { diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 74ceb43c713c..d318fc27a8ba 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -641,6 +641,18 @@ public: return false; } + /// isArcWeakrefUnavailable - Checks for a class or one of its super classes + /// to be incompatible with __weak references. Returns true if it is. + bool isArcWeakrefUnavailable() const { + const ObjCInterfaceDecl *Class = this; + while (Class) { + if (Class->hasAttr<ArcWeakrefUnavailableAttr>()) + return true; + Class = Class->getSuperClass(); + } + return false; + } + ObjCIvarDecl *lookupInstanceVariable(IdentifierInfo *IVarName, ObjCInterfaceDecl *&ClassDeclared); ObjCIvarDecl *lookupInstanceVariable(IdentifierInfo *IVarName) { @@ -1240,6 +1252,9 @@ class ObjCImplementationDecl : public ObjCImplDecl { /// IvarInitializers - The arguments used to initialize the ivars CXXCtorInitializer **IvarInitializers; unsigned NumIvarInitializers; + + /// true if class has a .cxx_[construct,destruct] method. + bool HasCXXStructors : 1; /// true of class extension has at least one bitfield ivar. bool HasSynthBitfield : 1; @@ -1249,7 +1264,7 @@ class ObjCImplementationDecl : public ObjCImplDecl { ObjCInterfaceDecl *superDecl) : ObjCImplDecl(ObjCImplementation, DC, L, classInterface), SuperClass(superDecl), IvarInitializers(0), NumIvarInitializers(0), - HasSynthBitfield(false) {} + HasCXXStructors(false), HasSynthBitfield(false) {} public: static ObjCImplementationDecl *Create(ASTContext &C, DeclContext *DC, SourceLocation L, @@ -1287,6 +1302,9 @@ public: void setIvarInitializers(ASTContext &C, CXXCtorInitializer ** initializers, unsigned numInitializers); + + bool hasCXXStructors() const { return HasCXXStructors; } + void setHasCXXStructors(bool val) { HasCXXStructors = val; } bool hasSynthBitfield() const { return HasSynthBitfield; } void setHasSynthBitfield (bool val) { HasSynthBitfield = val; } @@ -1393,7 +1411,16 @@ public: OBJC_PR_copy = 0x20, OBJC_PR_nonatomic = 0x40, OBJC_PR_setter = 0x80, - OBJC_PR_atomic = 0x100 + OBJC_PR_atomic = 0x100, + OBJC_PR_weak = 0x200, + OBJC_PR_strong = 0x400, + OBJC_PR_unsafe_unretained = 0x800 + // Adding a property should change NumPropertyAttrsBits + }; + + enum { + /// \brief Number of bits fitting all the property attributes. + NumPropertyAttrsBits = 12 }; enum SetterKind { Assign, Retain, Copy }; @@ -1401,8 +1428,8 @@ public: private: SourceLocation AtLoc; // location of @property TypeSourceInfo *DeclType; - unsigned PropertyAttributes : 9; - unsigned PropertyAttributesAsWritten : 9; + unsigned PropertyAttributes : NumPropertyAttrsBits; + unsigned PropertyAttributesAsWritten : NumPropertyAttrsBits; // @required/@optional unsigned PropertyImplementation : 2; @@ -1445,6 +1472,12 @@ public: PropertyAttributeKind getPropertyAttributesAsWritten() const { return PropertyAttributeKind(PropertyAttributesAsWritten); } + + bool hasWrittenStorageAttribute() const { + return PropertyAttributesAsWritten & (OBJC_PR_assign | OBJC_PR_copy | + OBJC_PR_unsafe_unretained | OBJC_PR_retain | OBJC_PR_strong | + OBJC_PR_weak); + } void setPropertyAttributesAsWritten(PropertyAttributeKind PRVal) { PropertyAttributesAsWritten = PRVal; @@ -1466,7 +1499,7 @@ public: /// the property setter. This is only valid if the property has been /// defined to have a setter. SetterKind getSetterKind() const { - if (PropertyAttributes & OBJC_PR_retain) + if (PropertyAttributes & (OBJC_PR_retain|OBJC_PR_strong)) return Retain; if (PropertyAttributes & OBJC_PR_copy) return Copy; diff --git a/include/clang/AST/DeclTemplate.h b/include/clang/AST/DeclTemplate.h index dc50d614bf7c..d2b1d3990c66 100644 --- a/include/clang/AST/DeclTemplate.h +++ b/include/clang/AST/DeclTemplate.h @@ -314,6 +314,10 @@ public: return (TemplateSpecializationKind)(Template.getInt() + 1); } + bool isExplicitSpecialization() const { + return getTemplateSpecializationKind() == TSK_ExplicitSpecialization; + } + /// \brief Set the template specialization kind. void setTemplateSpecializationKind(TemplateSpecializationKind TSK) { assert(TSK != TSK_Undeclared && @@ -1398,6 +1402,10 @@ public: return static_cast<TemplateSpecializationKind>(SpecializationKind); } + bool isExplicitSpecialization() const { + return getSpecializationKind() == TSK_ExplicitSpecialization; + } + void setSpecializationKind(TemplateSpecializationKind TSK) { SpecializationKind = TSK; } diff --git a/include/clang/AST/DeclarationName.h b/include/clang/AST/DeclarationName.h index e54719b33d30..bb098313ea3a 100644 --- a/include/clang/AST/DeclarationName.h +++ b/include/clang/AST/DeclarationName.h @@ -492,6 +492,9 @@ public: LocInfo.CXXLiteralOperatorName.OpNameLoc = Loc.getRawEncoding(); } + /// \brief Determine whether this name involves a template parameter. + bool isInstantiationDependent() const; + /// \brief Determine whether this name contains an unexpanded /// parameter pack. bool containsUnexpandedParameterPack() const; diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index ce86458ed4f4..c7f870725c42 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -57,9 +57,12 @@ class Expr : public Stmt { protected: Expr(StmtClass SC, QualType T, ExprValueKind VK, ExprObjectKind OK, - bool TD, bool VD, bool ContainsUnexpandedParameterPack) : Stmt(SC) { + bool TD, bool VD, bool ID, bool ContainsUnexpandedParameterPack) + : Stmt(SC) + { ExprBits.TypeDependent = TD; ExprBits.ValueDependent = VD; + ExprBits.InstantiationDependent = ID; ExprBits.ValueKind = VK; ExprBits.ObjectKind = OK; ExprBits.ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; @@ -95,7 +98,11 @@ public: bool isValueDependent() const { return ExprBits.ValueDependent; } /// \brief Set whether this expression is value-dependent or not. - void setValueDependent(bool VD) { ExprBits.ValueDependent = VD; } + void setValueDependent(bool VD) { + ExprBits.ValueDependent = VD; + if (VD) + ExprBits.InstantiationDependent = true; + } /// isTypeDependent - Determines whether this expression is /// type-dependent (C++ [temp.dep.expr]), which means that its type @@ -111,7 +118,37 @@ public: bool isTypeDependent() const { return ExprBits.TypeDependent; } /// \brief Set whether this expression is type-dependent or not. - void setTypeDependent(bool TD) { ExprBits.TypeDependent = TD; } + void setTypeDependent(bool TD) { + ExprBits.TypeDependent = TD; + if (TD) + ExprBits.InstantiationDependent = true; + } + + /// \brief Whether this expression is instantiation-dependent, meaning that + /// it depends in some way on a template parameter, even if neither its type + /// nor (constant) value can change due to the template instantiation. + /// + /// In the following example, the expression \c sizeof(sizeof(T() + T())) is + /// instantiation-dependent (since it involves a template parameter \c T), but + /// is neither type- nor value-dependent, since the type of the inner + /// \c sizeof is known (\c std::size_t) and therefore the size of the outer + /// \c sizeof is known. + /// + /// \code + /// template<typename T> + /// void f(T x, T y) { + /// sizeof(sizeof(T() + T()); + /// } + /// \endcode + /// + bool isInstantiationDependent() const { + return ExprBits.InstantiationDependent; + } + + /// \brief Set whether this expression is instantiation-dependent or not. + void setInstantiationDependent(bool ID) { + ExprBits.InstantiationDependent = ID; + } /// \brief Whether this expression contains an unexpanded parameter /// pack (for C++0x variadic templates). @@ -501,6 +538,14 @@ public: /// the rules of C++ [expr.unary.noexcept]. CanThrowResult CanThrow(ASTContext &C) const; + /// IgnoreImpCasts - Skip past any implicit casts which might + /// surround this expression. Only skips ImplicitCastExprs. + Expr *IgnoreImpCasts(); + + /// IgnoreImplicit - Skip past any implicit AST nodes which might + /// surround this expression. + Expr *IgnoreImplicit() { return cast<Expr>(Stmt::IgnoreImplicit()); } + /// IgnoreParens - Ignore parentheses. If this Expr is a ParenExpr, return /// its subexpression. If that subexpression is also a ParenExpr, /// then this method recursively returns its subexpression, and so forth. @@ -555,7 +600,10 @@ public: /// \brief Whether this expression is an implicit reference to 'this' in C++. bool isImplicitCXXThis() const; - + + const Expr *IgnoreImpCasts() const { + return const_cast<Expr*>(this)->IgnoreImpCasts(); + } const Expr *IgnoreParens() const { return const_cast<Expr*>(this)->IgnoreParens(); } @@ -595,7 +643,9 @@ public: OpaqueValueExpr(SourceLocation Loc, QualType T, ExprValueKind VK, ExprObjectKind OK = OK_Ordinary) : Expr(OpaqueValueExprClass, T, VK, OK, - T->isDependentType(), T->isDependentType(), false), + T->isDependentType(), T->isDependentType(), + T->isInstantiationDependentType(), + false), SourceExpr(0), Loc(Loc) { } @@ -664,7 +714,8 @@ struct ExplicitTemplateArgumentList { void initializeFrom(const TemplateArgumentListInfo &List); void initializeFrom(const TemplateArgumentListInfo &List, - bool &Dependent, bool &ContainsUnexpandedParameterPack); + bool &Dependent, bool &InstantiationDependent, + bool &ContainsUnexpandedParameterPack); void copyInto(TemplateArgumentListInfo &List) const; static std::size_t sizeFor(unsigned NumTemplateArgs); static std::size_t sizeFor(const TemplateArgumentListInfo &List); @@ -746,9 +797,10 @@ class DeclRefExpr : public Expr { void computeDependence(); public: - DeclRefExpr(ValueDecl *D, QualType T, ExprValueKind VK, SourceLocation L) - : Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false), - D(D), Loc(L) { + DeclRefExpr(ValueDecl *D, QualType T, ExprValueKind VK, SourceLocation L, + const DeclarationNameLoc &LocInfo = DeclarationNameLoc()) + : Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false, false), + D(D), Loc(L), DNLoc(LocInfo) { DeclRefExprBits.HasQualifier = 0; DeclRefExprBits.HasExplicitTemplateArgs = 0; DeclRefExprBits.HasFoundDecl = 0; @@ -936,6 +988,7 @@ public: PredefinedExpr(SourceLocation l, QualType type, IdentType IT) : Expr(PredefinedExprClass, type, VK_LValue, OK_Ordinary, type->isDependentType(), type->isDependentType(), + type->isInstantiationDependentType(), /*ContainsUnexpandedParameterPack=*/false), Loc(l), Type(IT) {} @@ -1023,7 +1076,7 @@ public: IntegerLiteral(ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l) : Expr(IntegerLiteralClass, type, VK_RValue, OK_Ordinary, false, false, - false), + false, false), Loc(l) { assert(type->isIntegerType() && "Illegal type in IntegerLiteral"); assert(V.getBitWidth() == C.getIntWidth(type) && @@ -1066,7 +1119,7 @@ public: // type should be IntTy CharacterLiteral(unsigned value, bool iswide, QualType type, SourceLocation l) : Expr(CharacterLiteralClass, type, VK_RValue, OK_Ordinary, false, false, - false), + false, false), Value(value), Loc(l), IsWide(iswide) { } @@ -1101,7 +1154,7 @@ class FloatingLiteral : public Expr { FloatingLiteral(ASTContext &C, const llvm::APFloat &V, bool isexact, QualType Type, SourceLocation L) : Expr(FloatingLiteralClass, Type, VK_RValue, OK_Ordinary, false, false, - false), + false, false), IsExact(isexact), Loc(L) { setValue(C, V); } @@ -1152,7 +1205,7 @@ class ImaginaryLiteral : public Expr { public: ImaginaryLiteral(Expr *val, QualType Ty) : Expr(ImaginaryLiteralClass, Ty, VK_RValue, OK_Ordinary, false, false, - false), + false, false), Val(val) {} /// \brief Build an empty imaginary literal. @@ -1200,21 +1253,20 @@ class StringLiteral : public Expr { SourceLocation TokLocs[1]; StringLiteral(QualType Ty) : - Expr(StringLiteralClass, Ty, VK_LValue, OK_Ordinary, false, false, false) {} + Expr(StringLiteralClass, Ty, VK_LValue, OK_Ordinary, false, false, false, + false) {} public: /// This is the "fully general" constructor that allows representation of /// strings formed from multiple concatenated tokens. - static StringLiteral *Create(ASTContext &C, const char *StrData, - unsigned ByteLength, bool Wide, bool Pascal, - QualType Ty, + static StringLiteral *Create(ASTContext &C, llvm::StringRef Str, bool Wide, + bool Pascal, QualType Ty, const SourceLocation *Loc, unsigned NumStrs); /// Simple constructor for string literals made from one token. - static StringLiteral *Create(ASTContext &C, const char *StrData, - unsigned ByteLength, bool Wide, + static StringLiteral *Create(ASTContext &C, llvm::StringRef Str, bool Wide, bool Pascal, QualType Ty, SourceLocation Loc) { - return Create(C, StrData, ByteLength, Wide, Pascal, Ty, &Loc, 1); + return Create(C, Str, Wide, Pascal, Ty, &Loc, 1); } /// \brief Construct an empty string literal. @@ -1289,6 +1341,7 @@ public: : Expr(ParenExprClass, val->getType(), val->getValueKind(), val->getObjectKind(), val->isTypeDependent(), val->isValueDependent(), + val->isInstantiationDependent(), val->containsUnexpandedParameterPack()), L(l), R(r), Val(val) {} @@ -1345,6 +1398,8 @@ public: : Expr(UnaryOperatorClass, type, VK, OK, input->isTypeDependent() || type->isDependentType(), input->isValueDependent(), + (input->isInstantiationDependent() || + type->isInstantiationDependentType()), input->containsUnexpandedParameterPack()), Opc(opc), Loc(l), Val(input) {} @@ -1640,6 +1695,7 @@ public: false, // Never type-dependent (C++ [temp.dep.expr]p3). // Value-dependent if the argument is type-dependent. TInfo->getType()->isDependentType(), + TInfo->getType()->isInstantiationDependentType(), TInfo->getType()->containsUnexpandedParameterPack()), Kind(ExprKind), isType(true), OpLoc(op), RParenLoc(rp) { Argument.Ty = TInfo; @@ -1652,6 +1708,7 @@ public: false, // Never type-dependent (C++ [temp.dep.expr]p3). // Value-dependent if the argument is type-dependent. E->isTypeDependent(), + E->isInstantiationDependent(), E->containsUnexpandedParameterPack()), Kind(ExprKind), isType(false), OpLoc(op), RParenLoc(rp) { Argument.Ex = E; @@ -1729,6 +1786,8 @@ public: : Expr(ArraySubscriptExprClass, t, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), + (lhs->isInstantiationDependent() || + rhs->isInstantiationDependent()), (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), RBracketLoc(rbracketloc) { @@ -1986,7 +2045,9 @@ public: const DeclarationNameInfo &NameInfo, QualType ty, ExprValueKind VK, ExprObjectKind OK) : Expr(MemberExprClass, ty, VK, OK, - base->isTypeDependent(), base->isValueDependent(), + base->isTypeDependent(), + base->isValueDependent(), + base->isInstantiationDependent(), base->containsUnexpandedParameterPack()), Base(base), MemberDecl(memberdecl), MemberLoc(NameInfo.getLoc()), MemberDNLoc(NameInfo.getInfo()), IsArrow(isarrow), @@ -2003,6 +2064,7 @@ public: ExprValueKind VK, ExprObjectKind OK) : Expr(MemberExprClass, ty, VK, OK, base->isTypeDependent(), base->isValueDependent(), + base->isInstantiationDependent(), base->containsUnexpandedParameterPack()), Base(base), MemberDecl(memberdecl), MemberLoc(l), MemberDNLoc(), IsArrow(isarrow), @@ -2188,6 +2250,8 @@ public: : Expr(CompoundLiteralExprClass, T, VK, OK_Ordinary, tinfo->getType()->isDependentType(), init->isValueDependent(), + (init->isInstantiationDependent() || + tinfo->getType()->isInstantiationDependentType()), init->containsUnexpandedParameterPack()), LParenLoc(lparenloc), TInfo(tinfo), Init(init), FileScope(fileScope) {} @@ -2276,6 +2340,9 @@ private: case CK_IntegralComplexToReal: case CK_IntegralComplexCast: case CK_IntegralComplexToFloatingComplex: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: assert(!getType()->isBooleanType() && "unheralded conversion to bool"); // fallthrough to check for null base path @@ -2318,6 +2385,8 @@ protected: // Cast expressions are value-dependent if the type is // dependent or if the subexpression is value-dependent. ty->isDependentType() || (op && op->isValueDependent()), + (ty->isInstantiationDependentType() || + (op && op->isInstantiationDependent())), (ty->containsUnexpandedParameterPack() || op->containsUnexpandedParameterPack())), Op(op) { @@ -2426,6 +2495,13 @@ public: static bool classof(const ImplicitCastExpr *) { return true; } }; +inline Expr *Expr::IgnoreImpCasts() { + Expr *e = this; + while (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(e)) + e = ice->getSubExpr(); + return e; +} + /// ExplicitCastExpr - An explicit cast written in the source /// code. /// @@ -2551,6 +2627,8 @@ public: : Expr(BinaryOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), + (lhs->isInstantiationDependent() || + rhs->isInstantiationDependent()), (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), Opc(opc), OpLoc(opLoc) { @@ -2653,6 +2731,8 @@ protected: : Expr(CompoundAssignOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), + (lhs->isInstantiationDependent() || + rhs->isInstantiationDependent()), (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), Opc(opc), OpLoc(opLoc) { @@ -2713,11 +2793,11 @@ class AbstractConditionalOperator : public Expr { protected: AbstractConditionalOperator(StmtClass SC, QualType T, ExprValueKind VK, ExprObjectKind OK, - bool TD, bool VD, + bool TD, bool VD, bool ID, bool ContainsUnexpandedParameterPack, SourceLocation qloc, SourceLocation cloc) - : Expr(SC, T, VK, OK, TD, VD, ContainsUnexpandedParameterPack), + : Expr(SC, T, VK, OK, TD, VD, ID, ContainsUnexpandedParameterPack), QuestionLoc(qloc), ColonLoc(cloc) {} AbstractConditionalOperator(StmtClass SC, EmptyShell Empty) @@ -2765,6 +2845,9 @@ public: (lhs->isTypeDependent() || rhs->isTypeDependent()), (cond->isValueDependent() || lhs->isValueDependent() || rhs->isValueDependent()), + (cond->isInstantiationDependent() || + lhs->isInstantiationDependent() || + rhs->isInstantiationDependent()), (cond->containsUnexpandedParameterPack() || lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack()), @@ -2833,6 +2916,8 @@ public: : AbstractConditionalOperator(BinaryConditionalOperatorClass, t, VK, OK, (common->isTypeDependent() || rhs->isTypeDependent()), (common->isValueDependent() || rhs->isValueDependent()), + (common->isInstantiationDependent() || + rhs->isInstantiationDependent()), (common->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack()), qloc, cloc), @@ -2914,7 +2999,8 @@ class AddrLabelExpr : public Expr { public: AddrLabelExpr(SourceLocation AALoc, SourceLocation LLoc, LabelDecl *L, QualType t) - : Expr(AddrLabelExprClass, t, VK_RValue, OK_Ordinary, false, false, false), + : Expr(AddrLabelExprClass, t, VK_RValue, OK_Ordinary, false, false, false, + false), AmpAmpLoc(AALoc), LabelLoc(LLoc), Label(L) {} /// \brief Build an empty address of a label expression. @@ -2953,10 +3039,12 @@ class StmtExpr : public Expr { SourceLocation LParenLoc, RParenLoc; public: // FIXME: Does type-dependence need to be computed differently? + // FIXME: Do we need to compute instantiation instantiation-dependence for + // statements? (ugh!) StmtExpr(CompoundStmt *substmt, QualType T, SourceLocation lp, SourceLocation rp) : Expr(StmtExprClass, T, VK_RValue, OK_Ordinary, - T->isDependentType(), false, false), + T->isDependentType(), false, false, false), SubStmt(substmt), LParenLoc(lp), RParenLoc(rp) { } /// \brief Build an empty statement expression. @@ -3073,6 +3161,9 @@ public: QualType t, ExprValueKind VK, ExprObjectKind OK, SourceLocation RP, bool TypeDependent, bool ValueDependent) : Expr(ChooseExprClass, t, VK, OK, TypeDependent, ValueDependent, + (cond->isInstantiationDependent() || + lhs->isInstantiationDependent() || + rhs->isInstantiationDependent()), (cond->containsUnexpandedParameterPack() || lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), @@ -3134,7 +3225,8 @@ class GNUNullExpr : public Expr { public: GNUNullExpr(QualType Ty, SourceLocation Loc) - : Expr(GNUNullExprClass, Ty, VK_RValue, OK_Ordinary, false, false, false), + : Expr(GNUNullExprClass, Ty, VK_RValue, OK_Ordinary, false, false, false, + false), TokenLoc(Loc) { } /// \brief Build an empty GNU __null expression. @@ -3166,6 +3258,8 @@ public: SourceLocation RPLoc, QualType t) : Expr(VAArgExprClass, t, VK_RValue, OK_Ordinary, t->isDependentType(), false, + (TInfo->getType()->isInstantiationDependentType() || + e->isInstantiationDependent()), (TInfo->getType()->containsUnexpandedParameterPack() || e->containsUnexpandedParameterPack())), Val(e), TInfo(TInfo), @@ -3537,9 +3631,9 @@ public: bool isArrayDesignator() const { return Kind == ArrayDesignator; } bool isArrayRangeDesignator() const { return Kind == ArrayRangeDesignator; } - IdentifierInfo * getFieldName(); + IdentifierInfo *getFieldName() const; - FieldDecl *getField() { + FieldDecl *getField() const { assert(Kind == FieldDesignator && "Only valid on a field designator"); if (Field.NameOrField & 0x01) return 0; @@ -3612,12 +3706,18 @@ public: unsigned size() const { return NumDesignators; } // Iterator access to the designators. - typedef Designator* designators_iterator; + typedef Designator *designators_iterator; designators_iterator designators_begin() { return Designators; } designators_iterator designators_end() { return Designators + NumDesignators; } + typedef const Designator *const_designators_iterator; + const_designators_iterator designators_begin() const { return Designators; } + const_designators_iterator designators_end() const { + return Designators + NumDesignators; + } + typedef std::reverse_iterator<designators_iterator> reverse_designators_iterator; reverse_designators_iterator designators_rbegin() { @@ -3627,6 +3727,15 @@ public: return reverse_designators_iterator(designators_begin()); } + typedef std::reverse_iterator<const_designators_iterator> + const_reverse_designators_iterator; + const_reverse_designators_iterator designators_rbegin() const { + return const_reverse_designators_iterator(designators_end()); + } + const_reverse_designators_iterator designators_rend() const { + return const_reverse_designators_iterator(designators_begin()); + } + Designator *getDesignator(unsigned Idx) { return &designators_begin()[Idx]; } void setDesignators(ASTContext &C, const Designator *Desigs, @@ -3708,7 +3817,7 @@ class ImplicitValueInitExpr : public Expr { public: explicit ImplicitValueInitExpr(QualType ty) : Expr(ImplicitValueInitExprClass, ty, VK_RValue, OK_Ordinary, - false, false, false) { } + false, false, ty->isInstantiationDependentType(), false) { } /// \brief Construct an empty implicit value initialization. explicit ImplicitValueInitExpr(EmptyShell Empty) @@ -3735,7 +3844,7 @@ class ParenListExpr : public Expr { public: ParenListExpr(ASTContext& C, SourceLocation lparenloc, Expr **exprs, - unsigned numexprs, SourceLocation rparenloc); + unsigned numexprs, SourceLocation rparenloc, QualType T); /// \brief Build an empty paren list. explicit ParenListExpr(EmptyShell Empty) : Expr(ParenListExprClass, Empty) { } @@ -3909,6 +4018,7 @@ public: : Expr(ExtVectorElementExprClass, ty, VK, (VK == VK_RValue ? OK_Ordinary : OK_VectorComponent), base->isTypeDependent(), base->isValueDependent(), + base->isInstantiationDependent(), base->containsUnexpandedParameterPack()), Base(base), Accessor(&accessor), AccessorLoc(loc) {} @@ -3963,7 +4073,10 @@ protected: public: BlockExpr(BlockDecl *BD, QualType ty) : Expr(BlockExprClass, ty, VK_RValue, OK_Ordinary, - ty->isDependentType(), false, false), + ty->isDependentType(), false, + // FIXME: Check for instantiate-dependence in the statement? + ty->isInstantiationDependentType(), + false), TheBlock(BD) {} /// \brief Build an empty block expression. @@ -4037,26 +4150,36 @@ public: /// AsTypeExpr - Clang builtin function __builtin_astype [OpenCL 6.2.4.2] /// This AST node provides support for reinterpreting a type to another /// type of the same size. -class AsTypeExpr : public Expr { +class AsTypeExpr : public Expr { // Should this be an ExplicitCastExpr? private: - Expr* SrcExpr; - QualType DstType; + Stmt *SrcExpr; SourceLocation BuiltinLoc, RParenLoc; + + friend class ASTReader; + friend class ASTStmtReader; + explicit AsTypeExpr(EmptyShell Empty) : Expr(AsTypeExprClass, Empty) {} public: AsTypeExpr(Expr* SrcExpr, QualType DstType, ExprValueKind VK, ExprObjectKind OK, SourceLocation BuiltinLoc, SourceLocation RParenLoc) - : Expr(AsTypeExprClass, DstType, VK, OK, false, false, false), - SrcExpr(SrcExpr), DstType(DstType), - BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) {} - - /// \brief Build an empty __builtin_astype - explicit AsTypeExpr(EmptyShell Empty) : Expr(AsTypeExprClass, Empty) {} + : Expr(AsTypeExprClass, DstType, VK, OK, + DstType->isDependentType(), + DstType->isDependentType() || SrcExpr->isValueDependent(), + (DstType->isInstantiationDependentType() || + SrcExpr->isInstantiationDependent()), + (DstType->containsUnexpandedParameterPack() || + SrcExpr->containsUnexpandedParameterPack())), + SrcExpr(SrcExpr), BuiltinLoc(BuiltinLoc), RParenLoc(RParenLoc) {} /// getSrcExpr - Return the Expr to be converted. - Expr *getSrcExpr() const { return SrcExpr; } - QualType getDstType() const { return DstType; } + Expr *getSrcExpr() const { return cast<Expr>(SrcExpr); } + + /// getBuiltinLoc - Return the location of the __builtin_astype token. + SourceLocation getBuiltinLoc() const { return BuiltinLoc; } + + /// getRParenLoc - Return the location of final right parenthesis. + SourceLocation getRParenLoc() const { return RParenLoc; } SourceRange getSourceRange() const { return SourceRange(BuiltinLoc, RParenLoc); @@ -4068,7 +4191,7 @@ public: static bool classof(const AsTypeExpr *) { return true; } // Iterators - child_range children() { return child_range(); } + child_range children() { return child_range(&SrcExpr, &SrcExpr+1); } }; } // end namespace clang diff --git a/include/clang/AST/ExprCXX.h b/include/clang/AST/ExprCXX.h index a97057973745..19117040ef96 100644 --- a/include/clang/AST/ExprCXX.h +++ b/include/clang/AST/ExprCXX.h @@ -330,7 +330,7 @@ class CXXBoolLiteralExpr : public Expr { public: CXXBoolLiteralExpr(bool val, QualType Ty, SourceLocation l) : Expr(CXXBoolLiteralExprClass, Ty, VK_RValue, OK_Ordinary, false, false, - false), + false, false), Value(val), Loc(l) {} explicit CXXBoolLiteralExpr(EmptyShell Empty) @@ -359,7 +359,7 @@ class CXXNullPtrLiteralExpr : public Expr { public: CXXNullPtrLiteralExpr(QualType Ty, SourceLocation l) : Expr(CXXNullPtrLiteralExprClass, Ty, VK_RValue, OK_Ordinary, false, false, - false), + false, false), Loc(l) {} explicit CXXNullPtrLiteralExpr(EmptyShell Empty) @@ -395,6 +395,7 @@ public: false, // typeid is value-dependent if the type or expression are dependent Operand->getType()->isDependentType(), + Operand->getType()->isInstantiationDependentType(), Operand->getType()->containsUnexpandedParameterPack()), Operand(Operand), Range(R) { } @@ -404,6 +405,7 @@ public: false, // typeid is value-dependent if the type or expression are dependent Operand->isTypeDependent() || Operand->isValueDependent(), + Operand->isInstantiationDependent(), Operand->containsUnexpandedParameterPack()), Operand(Operand), Range(R) { } @@ -471,12 +473,14 @@ public: CXXUuidofExpr(QualType Ty, TypeSourceInfo *Operand, SourceRange R) : Expr(CXXUuidofExprClass, Ty, VK_LValue, OK_Ordinary, false, Operand->getType()->isDependentType(), + Operand->getType()->isInstantiationDependentType(), Operand->getType()->containsUnexpandedParameterPack()), Operand(Operand), Range(R) { } CXXUuidofExpr(QualType Ty, Expr *Operand, SourceRange R) : Expr(CXXUuidofExprClass, Ty, VK_LValue, OK_Ordinary, false, Operand->isTypeDependent(), + Operand->isInstantiationDependent(), Operand->containsUnexpandedParameterPack()), Operand(Operand), Range(R) { } @@ -552,6 +556,7 @@ public: // 'this' is type-dependent if the class type of the enclosing // member function is dependent (C++ [temp.dep.expr]p2) Type->isDependentType(), Type->isDependentType(), + Type->isInstantiationDependentType(), /*ContainsUnexpandedParameterPack=*/false), Loc(L), Implicit(isImplicit) { } @@ -581,23 +586,35 @@ public: class CXXThrowExpr : public Expr { Stmt *Op; SourceLocation ThrowLoc; + /// \brief Whether the thrown variable (if any) is in scope. + unsigned IsThrownVariableInScope : 1; + + friend class ASTStmtReader; + public: // Ty is the void type which is used as the result type of the // exepression. The l is the location of the throw keyword. expr // can by null, if the optional expression to throw isn't present. - CXXThrowExpr(Expr *expr, QualType Ty, SourceLocation l) : + CXXThrowExpr(Expr *expr, QualType Ty, SourceLocation l, + bool IsThrownVariableInScope) : Expr(CXXThrowExprClass, Ty, VK_RValue, OK_Ordinary, false, false, + expr && expr->isInstantiationDependent(), expr && expr->containsUnexpandedParameterPack()), - Op(expr), ThrowLoc(l) {} + Op(expr), ThrowLoc(l), IsThrownVariableInScope(IsThrownVariableInScope) {} CXXThrowExpr(EmptyShell Empty) : Expr(CXXThrowExprClass, Empty) {} const Expr *getSubExpr() const { return cast_or_null<Expr>(Op); } Expr *getSubExpr() { return cast_or_null<Expr>(Op); } - void setSubExpr(Expr *E) { Op = E; } SourceLocation getThrowLoc() const { return ThrowLoc; } - void setThrowLoc(SourceLocation L) { ThrowLoc = L; } + /// \brief Determines whether the variable thrown by this expression (if any!) + /// is within the innermost try block. + /// + /// This information is required to determine whether the NRVO can apply to + /// this variable. + bool isThrownVariableInScope() const { return IsThrownVariableInScope; } + SourceRange getSourceRange() const { if (getSubExpr() == 0) return SourceRange(ThrowLoc, ThrowLoc); @@ -636,14 +653,14 @@ class CXXDefaultArgExpr : public Expr { ? param->getType().getNonReferenceType() : param->getDefaultArg()->getType(), param->getDefaultArg()->getValueKind(), - param->getDefaultArg()->getObjectKind(), false, false, false), + param->getDefaultArg()->getObjectKind(), false, false, false, false), Param(param, false), Loc(Loc) { } CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *param, Expr *SubExpr) : Expr(SC, SubExpr->getType(), SubExpr->getValueKind(), SubExpr->getObjectKind(), - false, false, false), + false, false, false, false), Param(param, true), Loc(Loc) { *reinterpret_cast<Expr **>(this + 1) = SubExpr; } @@ -742,6 +759,7 @@ class CXXBindTemporaryExpr : public Expr { : Expr(CXXBindTemporaryExprClass, SubExpr->getType(), VK_RValue, OK_Ordinary, SubExpr->isTypeDependent(), SubExpr->isValueDependent(), + SubExpr->isInstantiationDependent(), SubExpr->containsUnexpandedParameterPack()), Temp(temp), SubExpr(SubExpr) { } @@ -995,7 +1013,7 @@ public: TypeSourceInfo *TypeInfo, SourceLocation rParenLoc ) : Expr(CXXScalarValueInitExprClass, Type, VK_RValue, OK_Ordinary, - false, false, false), + false, false, Type->isInstantiationDependentType(), false), RParenLoc(rParenLoc), TypeInfo(TypeInfo) {} explicit CXXScalarValueInitExpr(EmptyShell Shell) @@ -1241,6 +1259,7 @@ public: bool arrayFormAsWritten, bool usualArrayDeleteWantsSize, FunctionDecl *operatorDelete, Expr *arg, SourceLocation loc) : Expr(CXXDeleteExprClass, ty, VK_RValue, OK_Ordinary, false, false, + arg->isInstantiationDependent(), arg->containsUnexpandedParameterPack()), GlobalDelete(globalDelete), ArrayForm(arrayForm), ArrayFormAsWritten(arrayFormAsWritten), @@ -1500,6 +1519,7 @@ public: SourceLocation rparen, QualType ty) : Expr(UnaryTypeTraitExprClass, ty, VK_RValue, OK_Ordinary, false, queried->getType()->isDependentType(), + queried->getType()->isInstantiationDependentType(), queried->getType()->containsUnexpandedParameterPack()), UTT(utt), Value(value), Loc(loc), RParen(rparen), QueriedType(queried) { } @@ -1558,6 +1578,8 @@ public: : Expr(BinaryTypeTraitExprClass, ty, VK_RValue, OK_Ordinary, false, lhsType->getType()->isDependentType() || rhsType->getType()->isDependentType(), + (lhsType->getType()->isInstantiationDependentType() || + rhsType->getType()->isInstantiationDependentType()), (lhsType->getType()->containsUnexpandedParameterPack() || rhsType->getType()->containsUnexpandedParameterPack())), BTT(btt), Value(value), Loc(loc), RParen(rparen), @@ -1625,6 +1647,8 @@ public: Expr *dimension, SourceLocation rparen, QualType ty) : Expr(ArrayTypeTraitExprClass, ty, VK_RValue, OK_Ordinary, false, queried->getType()->isDependentType(), + (queried->getType()->isInstantiationDependentType() || + (dimension && dimension->isInstantiationDependent())), queried->getType()->containsUnexpandedParameterPack()), ATT(att), Value(value), Dimension(dimension), Loc(loc), RParen(rparen), QueriedType(queried) { } @@ -1684,6 +1708,7 @@ public: false, // Not type-dependent // Value-dependent if the argument is type-dependent. queried->isTypeDependent(), + queried->isInstantiationDependent(), queried->containsUnexpandedParameterPack()), ET(et), Value(value), Loc(loc), RParen(rparen), QueriedExpression(queried) { } @@ -1736,8 +1761,9 @@ protected: const DeclarationNameInfo &NameInfo, const TemplateArgumentListInfo *TemplateArgs, UnresolvedSetIterator Begin, UnresolvedSetIterator End, - bool KnownDependent = false, - bool KnownContainsUnexpandedParameterPack = false); + bool KnownDependent, + bool KnownInstantiationDependent, + bool KnownContainsUnexpandedParameterPack); OverloadExpr(StmtClass K, EmptyShell Empty) : Expr(K, Empty), Results(0), NumResults(0), @@ -1880,7 +1906,7 @@ class UnresolvedLookupExpr : public OverloadExpr { UnresolvedSetIterator Begin, UnresolvedSetIterator End, bool StdIsAssociatedNamespace) : OverloadExpr(UnresolvedLookupExprClass, C, QualifierLoc, NameInfo, - TemplateArgs, Begin, End), + TemplateArgs, Begin, End, false, false, false), RequiresADL(RequiresADL), StdIsAssociatedNamespace(StdIsAssociatedNamespace), Overloaded(Overloaded), NamingClass(NamingClass) @@ -2727,6 +2753,7 @@ public: : Expr(CXXNoexceptExprClass, Ty, VK_RValue, OK_Ordinary, /*TypeDependent*/false, /*ValueDependent*/Val == CT_Dependent, + Val == CT_Dependent || Operand->isInstantiationDependent(), Operand->containsUnexpandedParameterPack()), Value(Val == CT_Cannot), Operand(Operand), Range(Keyword, RParen) { } @@ -2787,7 +2814,8 @@ public: llvm::Optional<unsigned> NumExpansions) : Expr(PackExpansionExprClass, T, Pattern->getValueKind(), Pattern->getObjectKind(), /*TypeDependent=*/true, - /*ValueDependent=*/true, /*ContainsUnexpandedParameterPack=*/false), + /*ValueDependent=*/true, /*InstantiationDependent=*/true, + /*ContainsUnexpandedParameterPack=*/false), EllipsisLoc(EllipsisLoc), NumExpansions(NumExpansions? *NumExpansions + 1 : 0), Pattern(Pattern) { } @@ -2874,6 +2902,7 @@ public: SourceLocation PackLoc, SourceLocation RParenLoc) : Expr(SizeOfPackExprClass, SizeType, VK_RValue, OK_Ordinary, /*TypeDependent=*/false, /*ValueDependent=*/true, + /*InstantiationDependent=*/true, /*ContainsUnexpandedParameterPack=*/false), OperatorLoc(OperatorLoc), PackLoc(PackLoc), RParenLoc(RParenLoc), Length(0), Pack(Pack) { } @@ -2885,6 +2914,7 @@ public: unsigned Length) : Expr(SizeOfPackExprClass, SizeType, VK_RValue, OK_Ordinary, /*TypeDependent=*/false, /*ValueDependent=*/false, + /*InstantiationDependent=*/false, /*ContainsUnexpandedParameterPack=*/false), OperatorLoc(OperatorLoc), PackLoc(PackLoc), RParenLoc(RParenLoc), Length(Length), Pack(Pack) { } @@ -2927,6 +2957,53 @@ public: child_range children() { return child_range(); } }; +/// \brief Represents a reference to a non-type template parameter +/// that has been substituted with a template argument. +class SubstNonTypeTemplateParmExpr : public Expr { + /// \brief The replaced parameter. + NonTypeTemplateParmDecl *Param; + + /// \brief The replacement expression. + Stmt *Replacement; + + /// \brief The location of the non-type template parameter reference. + SourceLocation NameLoc; + + friend class ASTReader; + friend class ASTStmtReader; + explicit SubstNonTypeTemplateParmExpr(EmptyShell Empty) + : Expr(SubstNonTypeTemplateParmExprClass, Empty) { } + +public: + SubstNonTypeTemplateParmExpr(QualType type, + ExprValueKind valueKind, + SourceLocation loc, + NonTypeTemplateParmDecl *param, + Expr *replacement) + : Expr(SubstNonTypeTemplateParmExprClass, type, valueKind, OK_Ordinary, + replacement->isTypeDependent(), replacement->isValueDependent(), + replacement->isInstantiationDependent(), + replacement->containsUnexpandedParameterPack()), + Param(param), Replacement(replacement), NameLoc(loc) {} + + SourceLocation getNameLoc() const { return NameLoc; } + SourceRange getSourceRange() const { return NameLoc; } + + Expr *getReplacement() const { return cast<Expr>(Replacement); } + + NonTypeTemplateParmDecl *getParameter() const { return Param; } + + static bool classof(const Stmt *s) { + return s->getStmtClass() == SubstNonTypeTemplateParmExprClass; + } + static bool classof(const SubstNonTypeTemplateParmExpr *) { + return true; + } + + // Iterators + child_range children() { return child_range(&Replacement, &Replacement+1); } +}; + /// \brief Represents a reference to a non-type template parameter pack that /// has been substituted with a non-template argument pack. /// @@ -2953,8 +3030,10 @@ class SubstNonTypeTemplateParmPackExpr : public Expr { /// \brief The location of the non-type template parameter pack reference. SourceLocation NameLoc; + friend class ASTReader; friend class ASTStmtReader; - friend class ASTStmtWriter; + explicit SubstNonTypeTemplateParmPackExpr(EmptyShell Empty) + : Expr(SubstNonTypeTemplateParmPackExprClass, Empty) { } public: SubstNonTypeTemplateParmPackExpr(QualType T, @@ -2962,9 +3041,6 @@ public: SourceLocation NameLoc, const TemplateArgument &ArgPack); - SubstNonTypeTemplateParmPackExpr(EmptyShell Empty) - : Expr(SubstNonTypeTemplateParmPackExprClass, Empty) { } - /// \brief Retrieve the non-type template parameter pack being substituted. NonTypeTemplateParmDecl *getParameterPack() const { return Param; } @@ -2987,6 +3063,66 @@ public: // Iterators child_range children() { return child_range(); } }; + +/// \brief Represents a prvalue temporary that written into memory so that +/// a reference can bind to it. +/// +/// Prvalue expressions are materialized when they need to have an address +/// in memory for a reference to bind to. This happens when binding a +/// reference to the result of a conversion, e.g., +/// +/// \code +/// const int &r = 1.0; +/// \endcode +/// +/// Here, 1.0 is implicitly converted to an \c int. That resulting \c int is +/// then materialized via a \c MaterializeTemporaryExpr, and the reference +/// binds to the temporary. \c MaterializeTemporaryExprs are always glvalues +/// (either an lvalue or an xvalue, depending on the kind of reference binding +/// to it), maintaining the invariant that references always bind to glvalues. +class MaterializeTemporaryExpr : public Expr { + /// \brief The temporary-generating expression whose value will be + /// materialized. + Stmt *Temporary; + + friend class ASTStmtReader; + friend class ASTStmtWriter; + +public: + MaterializeTemporaryExpr(QualType T, Expr *Temporary, + bool BoundToLvalueReference) + : Expr(MaterializeTemporaryExprClass, T, + BoundToLvalueReference? VK_LValue : VK_XValue, OK_Ordinary, + Temporary->isTypeDependent(), Temporary->isValueDependent(), + Temporary->isInstantiationDependent(), + Temporary->containsUnexpandedParameterPack()), + Temporary(Temporary) { } + + MaterializeTemporaryExpr(EmptyShell Empty) + : Expr(MaterializeTemporaryExprClass, Empty) { } + + /// \brief Retrieve the temporary-generating subexpression whose value will + /// be materialized into a glvalue. + Expr *GetTemporaryExpr() const { return reinterpret_cast<Expr *>(Temporary); } + + /// \brief Determine whether this materialized temporary is bound to an + /// lvalue reference; otherwise, it's bound to an rvalue reference. + bool isBoundToLvalueReference() const { + return getValueKind() == VK_LValue; + } + + SourceRange getSourceRange() const { return Temporary->getSourceRange(); } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == MaterializeTemporaryExprClass; + } + static bool classof(const MaterializeTemporaryExpr *) { + return true; + } + + // Iterators + child_range children() { return child_range(&Temporary, &Temporary + 1); } +}; } // end namespace clang diff --git a/include/clang/AST/ExprObjC.h b/include/clang/AST/ExprObjC.h index 8163923d62d1..49d4cfe67626 100644 --- a/include/clang/AST/ExprObjC.h +++ b/include/clang/AST/ExprObjC.h @@ -30,7 +30,7 @@ class ObjCStringLiteral : public Expr { public: ObjCStringLiteral(StringLiteral *SL, QualType T, SourceLocation L) : Expr(ObjCStringLiteralClass, T, VK_RValue, OK_Ordinary, false, false, - false), + false, false), String(SL), AtLoc(L) {} explicit ObjCStringLiteral(EmptyShell Empty) : Expr(ObjCStringLiteralClass, Empty) {} @@ -67,6 +67,7 @@ public: : Expr(ObjCEncodeExprClass, T, VK_LValue, OK_Ordinary, EncodedType->getType()->isDependentType(), EncodedType->getType()->isDependentType(), + EncodedType->getType()->isInstantiationDependentType(), EncodedType->getType()->containsUnexpandedParameterPack()), EncodedType(EncodedType), AtLoc(at), RParenLoc(rp) {} @@ -106,7 +107,7 @@ public: ObjCSelectorExpr(QualType T, Selector selInfo, SourceLocation at, SourceLocation rp) : Expr(ObjCSelectorExprClass, T, VK_RValue, OK_Ordinary, false, false, - false), + false, false), SelName(selInfo), AtLoc(at), RParenLoc(rp){} explicit ObjCSelectorExpr(EmptyShell Empty) : Expr(ObjCSelectorExprClass, Empty) {} @@ -146,7 +147,7 @@ public: ObjCProtocolExpr(QualType T, ObjCProtocolDecl *protocol, SourceLocation at, SourceLocation rp) : Expr(ObjCProtocolExprClass, T, VK_RValue, OK_Ordinary, false, false, - false), + false, false), TheProtocol(protocol), AtLoc(at), RParenLoc(rp) {} explicit ObjCProtocolExpr(EmptyShell Empty) : Expr(ObjCProtocolExprClass, Empty) {} @@ -186,6 +187,7 @@ public: bool arrow = false, bool freeIvar = false) : Expr(ObjCIvarRefExprClass, t, VK_LValue, OK_Ordinary, /*TypeDependent=*/false, base->isValueDependent(), + base->isInstantiationDependent(), base->containsUnexpandedParameterPack()), D(d), Loc(l), Base(base), IsArrow(arrow), IsFreeIvar(freeIvar) {} @@ -248,6 +250,7 @@ public: SourceLocation l, Expr *base) : Expr(ObjCPropertyRefExprClass, t, VK, OK, /*TypeDependent=*/false, base->isValueDependent(), + base->isInstantiationDependent(), base->containsUnexpandedParameterPack()), PropertyOrGetter(PD, false), Setter(0), IdLoc(l), ReceiverLoc(), Receiver(base) { @@ -257,7 +260,7 @@ public: ExprValueKind VK, ExprObjectKind OK, SourceLocation l, SourceLocation sl, QualType st) : Expr(ObjCPropertyRefExprClass, t, VK, OK, - /*TypeDependent=*/false, false, + /*TypeDependent=*/false, false, st->isInstantiationDependentType(), st->containsUnexpandedParameterPack()), PropertyOrGetter(PD, false), Setter(0), IdLoc(l), ReceiverLoc(sl), Receiver(st.getTypePtr()) { @@ -267,7 +270,7 @@ public: QualType T, ExprValueKind VK, ExprObjectKind OK, SourceLocation IdLoc, Expr *Base) : Expr(ObjCPropertyRefExprClass, T, VK, OK, false, - Base->isValueDependent(), + Base->isValueDependent(), Base->isInstantiationDependent(), Base->containsUnexpandedParameterPack()), PropertyOrGetter(Getter, true), Setter(Setter), IdLoc(IdLoc), ReceiverLoc(), Receiver(Base) { @@ -277,7 +280,7 @@ public: QualType T, ExprValueKind VK, ExprObjectKind OK, SourceLocation IdLoc, SourceLocation SuperLoc, QualType SuperTy) - : Expr(ObjCPropertyRefExprClass, T, VK, OK, false, false, false), + : Expr(ObjCPropertyRefExprClass, T, VK, OK, false, false, false, false), PropertyOrGetter(Getter, true), Setter(Setter), IdLoc(IdLoc), ReceiverLoc(SuperLoc), Receiver(SuperTy.getTypePtr()) { } @@ -286,7 +289,7 @@ public: QualType T, ExprValueKind VK, ExprObjectKind OK, SourceLocation IdLoc, SourceLocation ReceiverLoc, ObjCInterfaceDecl *Receiver) - : Expr(ObjCPropertyRefExprClass, T, VK, OK, false, false, false), + : Expr(ObjCPropertyRefExprClass, T, VK, OK, false, false, false, false), PropertyOrGetter(Getter, true), Setter(Setter), IdLoc(IdLoc), ReceiverLoc(ReceiverLoc), Receiver(Receiver) { } @@ -456,7 +459,11 @@ class ObjCMessageExpr : public Expr { /// /// When non-zero, we have a method declaration; otherwise, we just /// have a selector. - unsigned HasMethod : 8; + unsigned HasMethod : 1; + + /// \brief Whether this message send is a "delegate init call", + /// i.e. a call of an init method on self from within an init method. + unsigned IsDelegateInitCall : 1; /// \brief When the message expression is a send to 'super', this is /// the location of the 'super' keyword. @@ -476,7 +483,7 @@ class ObjCMessageExpr : public Expr { ObjCMessageExpr(EmptyShell Empty, unsigned NumArgs) : Expr(ObjCMessageExprClass, Empty), NumArgs(NumArgs), Kind(0), - HasMethod(0), SelectorOrMethod(0) { } + HasMethod(0), IsDelegateInitCall(0), SelectorOrMethod(0) { } ObjCMessageExpr(QualType T, ExprValueKind VK, SourceLocation LBracLoc, @@ -807,6 +814,12 @@ public: getArgs()[Arg] = ArgExpr; } + /// isDelegateInitCall - Answers whether this message send has been + /// tagged as a "delegate init call", i.e. a call to a method in the + /// -init family on self from within an -init method implementation. + bool isDelegateInitCall() const { return IsDelegateInitCall; } + void setDelegateInitCall(bool isDelegate) { IsDelegateInitCall = isDelegate; } + SourceLocation getLeftLoc() const { return LBracLoc; } SourceLocation getRightLoc() const { return RBracLoc; } SourceLocation getSelectorLoc() const { return SelectorLoc; } @@ -860,6 +873,7 @@ public: ObjCIsaExpr(Expr *base, bool isarrow, SourceLocation l, QualType ty) : Expr(ObjCIsaExprClass, ty, VK_LValue, OK_Ordinary, /*TypeDependent=*/false, base->isValueDependent(), + base->isInstantiationDependent(), /*ContainsUnexpandedParameterPack=*/false), Base(base), IsaMemberLoc(l), IsArrow(isarrow) {} @@ -892,6 +906,123 @@ public: child_range children() { return child_range(&Base, &Base+1); } }; + +/// ObjCIndirectCopyRestoreExpr - Represents the passing of a function +/// argument by indirect copy-restore in ARC. This is used to support +/// passing indirect arguments with the wrong lifetime, e.g. when +/// passing the address of a __strong local variable to an 'out' +/// parameter. This expression kind is only valid in an "argument" +/// position to some sort of call expression. +/// +/// The parameter must have type 'pointer to T', and the argument must +/// have type 'pointer to U', where T and U agree except possibly in +/// qualification. If the argument value is null, then a null pointer +/// is passed; otherwise it points to an object A, and: +/// 1. A temporary object B of type T is initialized, either by +/// zero-initialization (used when initializing an 'out' parameter) +/// or copy-initialization (used when initializing an 'inout' +/// parameter). +/// 2. The address of the temporary is passed to the function. +/// 3. If the call completes normally, A is move-assigned from B. +/// 4. Finally, A is destroyed immediately. +/// +/// Currently 'T' must be a retainable object lifetime and must be +/// __autoreleasing; this qualifier is ignored when initializing +/// the value. +class ObjCIndirectCopyRestoreExpr : public Expr { + Stmt *Operand; + + // unsigned ObjCIndirectCopyRestoreBits.ShouldCopy : 1; + + friend class ASTReader; + friend class ASTStmtReader; + + void setShouldCopy(bool shouldCopy) { + ObjCIndirectCopyRestoreExprBits.ShouldCopy = shouldCopy; + } + + explicit ObjCIndirectCopyRestoreExpr(EmptyShell Empty) + : Expr(ObjCIndirectCopyRestoreExprClass, Empty) { } + +public: + ObjCIndirectCopyRestoreExpr(Expr *operand, QualType type, bool shouldCopy) + : Expr(ObjCIndirectCopyRestoreExprClass, type, VK_LValue, OK_Ordinary, + operand->isTypeDependent(), operand->isValueDependent(), + operand->isInstantiationDependent(), + operand->containsUnexpandedParameterPack()), + Operand(operand) { + setShouldCopy(shouldCopy); + } + + Expr *getSubExpr() { return cast<Expr>(Operand); } + const Expr *getSubExpr() const { return cast<Expr>(Operand); } + + /// shouldCopy - True if we should do the 'copy' part of the + /// copy-restore. If false, the temporary will be zero-initialized. + bool shouldCopy() const { return ObjCIndirectCopyRestoreExprBits.ShouldCopy; } + + child_range children() { return child_range(&Operand, &Operand+1); } + + // Source locations are determined by the subexpression. + SourceRange getSourceRange() const { return Operand->getSourceRange(); } + SourceLocation getExprLoc() const { return getSubExpr()->getExprLoc(); } + + static bool classof(const Stmt *s) { + return s->getStmtClass() == ObjCIndirectCopyRestoreExprClass; + } + static bool classof(const ObjCIndirectCopyRestoreExpr *) { return true; } +}; + +/// \brief An Objective-C "bridged" cast expression, which casts between +/// Objective-C pointers and C pointers, transferring ownership in the process. +/// +/// \code +/// NSString *str = (__bridge_transfer NSString *)CFCreateString(); +/// \endcode +class ObjCBridgedCastExpr : public ExplicitCastExpr { + SourceLocation LParenLoc; + SourceLocation BridgeKeywordLoc; + unsigned Kind : 2; + + friend class ASTStmtReader; + friend class ASTStmtWriter; + +public: + ObjCBridgedCastExpr(SourceLocation LParenLoc, ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, TypeSourceInfo *TSInfo, + Expr *Operand) + : ExplicitCastExpr(ObjCBridgedCastExprClass, TSInfo->getType(), VK_RValue, + CK_BitCast, Operand, 0, TSInfo), + LParenLoc(LParenLoc), BridgeKeywordLoc(BridgeKeywordLoc), Kind(Kind) { } + + /// \brief Construct an empty Objective-C bridged cast. + explicit ObjCBridgedCastExpr(EmptyShell Shell) + : ExplicitCastExpr(ObjCBridgedCastExprClass, Shell, 0) { } + + SourceLocation getLParenLoc() const { return LParenLoc; } + + /// \brief Determine which kind of bridge is being performed via this cast. + ObjCBridgeCastKind getBridgeKind() const { + return static_cast<ObjCBridgeCastKind>(Kind); + } + + /// \brief Retrieve the kind of bridge being performed as a string. + llvm::StringRef getBridgeKindName() const; + + /// \brief The location of the bridge keyword. + SourceLocation getBridgeKeywordLoc() const { return BridgeKeywordLoc; } + + SourceRange getSourceRange() const { + return SourceRange(LParenLoc, getSubExpr()->getLocEnd()); + } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCBridgedCastExprClass; + } + static bool classof(const ObjCBridgedCastExpr *) { return true; } + +}; + } // end namespace clang #endif diff --git a/include/clang/AST/ExternalASTSource.h b/include/clang/AST/ExternalASTSource.h index 846813adf7c3..ef1f1618ba1d 100644 --- a/include/clang/AST/ExternalASTSource.h +++ b/include/clang/AST/ExternalASTSource.h @@ -32,6 +32,20 @@ class Selector; class Stmt; class TagDecl; +/// \brief Enumeration describing the result of loading information from +/// an external source. +enum ExternalLoadResult { + /// \brief Loading the external information has succeeded. + ELR_Success, + + /// \brief Loading the external information has failed. + ELR_Failure, + + /// \brief The external information has already been loaded, and therefore + /// no additional processing is required. + ELR_AlreadyLoaded +}; + /// \brief Abstract interface for external sources of AST nodes. /// /// External AST sources provide AST nodes constructed from some @@ -132,10 +146,10 @@ public: /// declaration kind is one we are looking for. If NULL, all declarations /// are returned. /// - /// \return true if an error occurred + /// \return an indication of whether the load succeeded or failed. /// /// The default implementation of this method is a no-op. - virtual bool FindExternalLexicalDecls(const DeclContext *DC, + virtual ExternalLoadResult FindExternalLexicalDecls(const DeclContext *DC, bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl<Decl*> &Result); @@ -143,14 +157,14 @@ public: /// DeclContext. /// /// \return true if an error occurred - bool FindExternalLexicalDecls(const DeclContext *DC, + ExternalLoadResult FindExternalLexicalDecls(const DeclContext *DC, llvm::SmallVectorImpl<Decl*> &Result) { return FindExternalLexicalDecls(DC, 0, Result); } template <typename DeclTy> - bool FindExternalLexicalDeclsBy(const DeclContext *DC, - llvm::SmallVectorImpl<Decl*> &Result) { + ExternalLoadResult FindExternalLexicalDeclsBy(const DeclContext *DC, + llvm::SmallVectorImpl<Decl*> &Result) { return FindExternalLexicalDecls(DC, DeclTy::classofKind, Result); } diff --git a/lib/CodeGen/GlobalDecl.h b/include/clang/AST/GlobalDecl.h index c2f36d210bfc..c43e44c26f31 100644 --- a/lib/CodeGen/GlobalDecl.h +++ b/include/clang/AST/GlobalDecl.h @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#ifndef CLANG_CODEGEN_GLOBALDECL_H -#define CLANG_CODEGEN_GLOBALDECL_H +#ifndef LLVM_CLANG_AST_GLOBALDECL_H +#define LLVM_CLANG_AST_GLOBALDECL_H #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" @@ -21,8 +21,6 @@ namespace clang { -namespace CodeGen { - /// GlobalDecl - represents a global declaration. This can either be a /// CXXConstructorDecl and the constructor type (Base, Complete). /// a CXXDestructorDecl and the destructor type (Base, Complete) or @@ -89,28 +87,27 @@ public: } }; -} // end namespace CodeGen } // end namespace clang namespace llvm { template<class> struct DenseMapInfo; - template<> struct DenseMapInfo<clang::CodeGen::GlobalDecl> { - static inline clang::CodeGen::GlobalDecl getEmptyKey() { - return clang::CodeGen::GlobalDecl(); + template<> struct DenseMapInfo<clang::GlobalDecl> { + static inline clang::GlobalDecl getEmptyKey() { + return clang::GlobalDecl(); } - static inline clang::CodeGen::GlobalDecl getTombstoneKey() { - return clang::CodeGen::GlobalDecl:: + static inline clang::GlobalDecl getTombstoneKey() { + return clang::GlobalDecl:: getFromOpaquePtr(reinterpret_cast<void*>(-1)); } - static unsigned getHashValue(clang::CodeGen::GlobalDecl GD) { + static unsigned getHashValue(clang::GlobalDecl GD) { return DenseMapInfo<void*>::getHashValue(GD.getAsOpaquePtr()); } - static bool isEqual(clang::CodeGen::GlobalDecl LHS, - clang::CodeGen::GlobalDecl RHS) { + static bool isEqual(clang::GlobalDecl LHS, + clang::GlobalDecl RHS) { return LHS == RHS; } @@ -119,7 +116,7 @@ namespace llvm { // GlobalDecl isn't *technically* a POD type. However, its copy constructor, // copy assignment operator, and destructor are all trivial. template <> - struct isPodLike<clang::CodeGen::GlobalDecl> { + struct isPodLike<clang::GlobalDecl> { static const bool value = true; }; } // end namespace llvm diff --git a/include/clang/AST/NestedNameSpecifier.h b/include/clang/AST/NestedNameSpecifier.h index c21c76b006ff..018041f8ba20 100644 --- a/include/clang/AST/NestedNameSpecifier.h +++ b/include/clang/AST/NestedNameSpecifier.h @@ -186,6 +186,10 @@ public: /// type or not. bool isDependent() const; + /// \brief Whether this nested name specifier involves a template + /// parameter. + bool isInstantiationDependent() const; + /// \brief Whether this nested-name-specifier contains an unexpanded /// parameter pack (for C++0x variadic templates). bool containsUnexpandedParameterPack() const; @@ -435,6 +439,14 @@ public: /// copied. NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const; + /// \brief Retrieve a nested-name-specifier with location + /// information based on the information in this builder. This loc + /// will contain references to the builder's internal data and may + /// be invalidated by any change to the builder. + NestedNameSpecifierLoc getTemporary() const { + return NestedNameSpecifierLoc(Representation, Buffer); + } + /// \brief Clear out this builder, and prepare it to build another /// nested-name-specifier with source-location information. void Clear() { diff --git a/include/clang/AST/OperationKinds.h b/include/clang/AST/OperationKinds.h index 35c72c45ce7c..92ff6041371c 100644 --- a/include/clang/AST/OperationKinds.h +++ b/include/clang/AST/OperationKinds.h @@ -245,7 +245,22 @@ enum CastKind { /// \brief Converts from an integral complex to a floating complex. /// _Complex unsigned -> _Complex float - CK_IntegralComplexToFloatingComplex + CK_IntegralComplexToFloatingComplex, + + /// \brief Produces a retainable object pointer so that it may be + /// consumed, e.g. by being passed to a consuming parameter. Calls + /// objc_retain. + CK_ObjCProduceObject, + + /// \brief Consumes a retainable object pointer that has just been + /// produced, e.g. as the return value of a retaining call. Enters + /// a cleanup to call objc_release at some indefinite time. + CK_ObjCConsumeObject, + + /// \brief Reclaim a retainable object pointer object that may have + /// been produced and autoreleased as part of a function return + /// sequence. + CK_ObjCReclaimReturnedObject }; #define CK_Invalid ((CastKind) -1) @@ -284,6 +299,19 @@ enum UnaryOperatorKind { UO_Extension // __extension__ marker. }; +/// \brief The kind of bridging performed by the Objective-C bridge cast. +enum ObjCBridgeCastKind { + /// \brief Bridging via __bridge, which does nothing but reinterpret + /// the bits. + OBC_Bridge, + /// \brief Bridging via __bridge_transfer, which transfers ownership of an + /// Objective-C pointer into ARC. + OBC_BridgeTransfer, + /// \brief Bridging via __bridge_retain, which makes an ARC object available + /// as a +1 C pointer. + OBC_BridgeRetained +}; + } #endif diff --git a/include/clang/AST/ParentMap.h b/include/clang/AST/ParentMap.h index 9ea5a0930d32..22c1e7269fac 100644 --- a/include/clang/AST/ParentMap.h +++ b/include/clang/AST/ParentMap.h @@ -32,6 +32,7 @@ public: Stmt *getParent(Stmt*) const; Stmt *getParentIgnoreParens(Stmt *) const; Stmt *getParentIgnoreParenCasts(Stmt *) const; + Stmt *getOuterParenParent(Stmt *) const; const Stmt *getParent(const Stmt* S) const { return getParent(const_cast<Stmt*>(S)); diff --git a/include/clang/AST/PrettyPrinter.h b/include/clang/AST/PrettyPrinter.h index cf5fadbd1850..fc8ac36b3b97 100644 --- a/include/clang/AST/PrettyPrinter.h +++ b/include/clang/AST/PrettyPrinter.h @@ -41,7 +41,7 @@ struct PrintingPolicy { SuppressTagKeyword(false), SuppressTag(false), SuppressScope(false), SuppressInitializers(false), Dump(false), ConstantArraySizeAsWritten(false), - AnonymousTagLocations(true) { } + AnonymousTagLocations(true), SuppressStrongLifetime(false) { } /// \brief The number of spaces to use to indent each line. unsigned Indentation : 8; @@ -129,6 +129,10 @@ struct PrintingPolicy { /// that entity (e.g., "enum <anonymous at t.h:10:5>"). Otherwise, just /// prints "<anonymous>" for the name. bool AnonymousTagLocations : 1; + + /// \brief When true, suppress printing of the __strong lifetime qualifier in + /// ARC. + unsigned SuppressStrongLifetime : 1; }; } // end namespace clang diff --git a/include/clang/AST/RecursiveASTVisitor.h b/include/clang/AST/RecursiveASTVisitor.h index a8f182a5bc92..85c5c08853d1 100644 --- a/include/clang/AST/RecursiveASTVisitor.h +++ b/include/clang/AST/RecursiveASTVisitor.h @@ -1721,6 +1721,7 @@ DEF_TRAVERSE_STMT(ObjCAtSynchronizedStmt, { }) DEF_TRAVERSE_STMT(ObjCAtThrowStmt, { }) DEF_TRAVERSE_STMT(ObjCAtTryStmt, { }) DEF_TRAVERSE_STMT(ObjCForCollectionStmt, { }) +DEF_TRAVERSE_STMT(ObjCAutoreleasePoolStmt, { }) DEF_TRAVERSE_STMT(CXXForRangeStmt, { }) DEF_TRAVERSE_STMT(ReturnStmt, { }) DEF_TRAVERSE_STMT(SwitchStmt, { }) @@ -1933,6 +1934,10 @@ DEF_TRAVERSE_STMT(ObjCMessageExpr, { }) DEF_TRAVERSE_STMT(ObjCPropertyRefExpr, { }) DEF_TRAVERSE_STMT(ObjCProtocolExpr, { }) DEF_TRAVERSE_STMT(ObjCSelectorExpr, { }) +DEF_TRAVERSE_STMT(ObjCIndirectCopyRestoreExpr, { }) +DEF_TRAVERSE_STMT(ObjCBridgedCastExpr, { + TRY_TO(TraverseTypeLoc(S->getTypeInfoAsWritten()->getTypeLoc())); +}) DEF_TRAVERSE_STMT(ParenExpr, { }) DEF_TRAVERSE_STMT(ParenListExpr, { }) DEF_TRAVERSE_STMT(PredefinedExpr, { }) @@ -1973,6 +1978,8 @@ DEF_TRAVERSE_STMT(CXXNoexceptExpr, { }) DEF_TRAVERSE_STMT(PackExpansionExpr, { }) DEF_TRAVERSE_STMT(SizeOfPackExpr, { }) DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmPackExpr, { }) +DEF_TRAVERSE_STMT(SubstNonTypeTemplateParmExpr, { }) +DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, { }) // These literals (all of them) do not need any action. DEF_TRAVERSE_STMT(IntegerLiteral, { }) diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h index 695fb0403ead..bf5f383be5e0 100644 --- a/include/clang/AST/Stmt.h +++ b/include/clang/AST/Stmt.h @@ -154,9 +154,10 @@ protected: unsigned ObjectKind : 2; unsigned TypeDependent : 1; unsigned ValueDependent : 1; + unsigned InstantiationDependent : 1; unsigned ContainsUnexpandedParameterPack : 1; }; - enum { NumExprBits = 15 }; + enum { NumExprBits = 16 }; class DeclRefExprBitfields { friend class DeclRefExpr; @@ -183,6 +184,13 @@ protected: unsigned NumPreArgs : 1; }; + class ObjCIndirectCopyRestoreExprBitfields { + friend class ObjCIndirectCopyRestoreExpr; + unsigned : NumExprBits; + + unsigned ShouldCopy : 1; + }; + union { // FIXME: this is wasteful on 64-bit platforms. void *Aligner; @@ -193,6 +201,7 @@ protected: DeclRefExprBitfields DeclRefExprBits; CastExprBitfields CastExprBits; CallExprBitfields CallExprBits; + ObjCIndirectCopyRestoreExprBitfields ObjCIndirectCopyRestoreExprBits; }; friend class ASTStmtReader; @@ -284,6 +293,10 @@ public: /// works on systems with GraphViz (Mac OS X) or dot+gv installed. void viewAST() const; + /// Skip past any implicit AST nodes which might surround this + /// statement, such as ExprWithCleanups or ImplicitCastExpr nodes. + Stmt *IgnoreImplicit(); + // Implement isa<T> support. static bool classof(const Stmt *) { return true; } @@ -327,7 +340,7 @@ public: /// declaration pointers) or the exact representation of the statement as /// written in the source. void Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, - bool Canonical); + bool Canonical) const; }; /// DeclStmt - Adaptor class for mixing declarations with statements and @@ -1458,6 +1471,10 @@ class SEHExceptStmt : public Stmt { Expr *FilterExpr, Stmt *Block); + friend class ASTReader; + friend class ASTStmtReader; + explicit SEHExceptStmt(EmptyShell E) : Stmt(SEHExceptStmtClass, E) { } + public: static SEHExceptStmt* Create(ASTContext &C, SourceLocation ExceptLoc, @@ -1492,6 +1509,10 @@ class SEHFinallyStmt : public Stmt { SEHFinallyStmt(SourceLocation Loc, Stmt *Block); + friend class ASTReader; + friend class ASTStmtReader; + explicit SEHFinallyStmt(EmptyShell E) : Stmt(SEHFinallyStmtClass, E) { } + public: static SEHFinallyStmt* Create(ASTContext &C, SourceLocation FinallyLoc, @@ -1530,6 +1551,10 @@ class SEHTryStmt : public Stmt { Stmt *TryBlock, Stmt *Handler); + friend class ASTReader; + friend class ASTStmtReader; + explicit SEHTryStmt(EmptyShell E) : Stmt(SEHTryStmtClass, E) { } + public: static SEHTryStmt* Create(ASTContext &C, bool isCXXTry, diff --git a/include/clang/AST/StmtObjC.h b/include/clang/AST/StmtObjC.h index 1800a71f9154..d996fc5cada3 100644 --- a/include/clang/AST/StmtObjC.h +++ b/include/clang/AST/StmtObjC.h @@ -342,6 +342,39 @@ public: child_range children() { return child_range(&Throw, &Throw+1); } }; +/// ObjCAutoreleasePoolStmt - This represent objective-c's +/// @autoreleasepool Statement +class ObjCAutoreleasePoolStmt : public Stmt { + Stmt *SubStmt; + SourceLocation AtLoc; +public: + ObjCAutoreleasePoolStmt(SourceLocation atLoc, + Stmt *subStmt) + : Stmt(ObjCAutoreleasePoolStmtClass), + SubStmt(subStmt), AtLoc(atLoc) {} + + explicit ObjCAutoreleasePoolStmt(EmptyShell Empty) : + Stmt(ObjCAutoreleasePoolStmtClass, Empty) { } + + const Stmt *getSubStmt() const { return SubStmt; } + Stmt *getSubStmt() { return SubStmt; } + void setSubStmt(Stmt *S) { SubStmt = S; } + + SourceRange getSourceRange() const { + return SourceRange(AtLoc, SubStmt->getLocEnd()); + } + + SourceLocation getAtLoc() const { return AtLoc; } + void setAtLoc(SourceLocation Loc) { AtLoc = Loc; } + + static bool classof(const Stmt *T) { + return T->getStmtClass() == ObjCAutoreleasePoolStmtClass; + } + static bool classof(const ObjCAutoreleasePoolStmt *) { return true; } + + child_range children() { return child_range(&SubStmt, &SubStmt + 1); } +}; + } // end namespace clang #endif diff --git a/include/clang/AST/TemplateBase.h b/include/clang/AST/TemplateBase.h index 821b4fcbb168..1c693e00c8cc 100644 --- a/include/clang/AST/TemplateBase.h +++ b/include/clang/AST/TemplateBase.h @@ -235,9 +235,14 @@ public: bool isNull() const { return Kind == Null; } /// \brief Whether this template argument is dependent on a template - /// parameter. + /// parameter such that its result can change from one instantiation to + /// another. bool isDependent() const; + /// \brief Whether this template argument is dependent on a template + /// parameter. + bool isInstantiationDependent() const; + /// \brief Whether this template argument contains an unexpanded /// parameter pack. bool containsUnexpandedParameterPack() const; diff --git a/include/clang/AST/TemplateName.h b/include/clang/AST/TemplateName.h index 1721973e8229..a180f587eddd 100644 --- a/include/clang/AST/TemplateName.h +++ b/include/clang/AST/TemplateName.h @@ -33,6 +33,7 @@ class OverloadedTemplateStorage; struct PrintingPolicy; class QualifiedTemplateName; class NamedDecl; +class SubstTemplateTemplateParmStorage; class SubstTemplateTemplateParmPackStorage; class TemplateArgument; class TemplateDecl; @@ -42,38 +43,49 @@ class TemplateTemplateParmDecl; /// template names or an already-substituted template template parameter pack. class UncommonTemplateNameStorage { protected: + enum Kind { + Overloaded, + SubstTemplateTemplateParm, + SubstTemplateTemplateParmPack + }; + union { struct { - /// \brief If true, this is an OverloadedTemplateStorage instance; - /// otherwise, it's a SubstTemplateTemplateParmPackStorage instance. - unsigned IsOverloadedStorage : 1; + /// \brief A Kind. + unsigned Kind : 2; /// \brief The number of stored templates or template arguments, /// depending on which subclass we have. - unsigned Size : 31; + unsigned Size : 30; } Bits; void *PointerAlignment; }; - UncommonTemplateNameStorage(unsigned Size, bool OverloadedStorage) { - Bits.IsOverloadedStorage = OverloadedStorage; - Bits.Size = Size; + UncommonTemplateNameStorage(Kind kind, unsigned size) { + Bits.Kind = kind; + Bits.Size = size; } public: unsigned size() const { return Bits.Size; } OverloadedTemplateStorage *getAsOverloadedStorage() { - return Bits.IsOverloadedStorage + return Bits.Kind == Overloaded ? reinterpret_cast<OverloadedTemplateStorage *>(this) : 0; } + SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() { + return Bits.Kind == SubstTemplateTemplateParm + ? reinterpret_cast<SubstTemplateTemplateParmStorage *>(this) + : 0; + } + SubstTemplateTemplateParmPackStorage *getAsSubstTemplateTemplateParmPack() { - return Bits.IsOverloadedStorage - ? 0 - : reinterpret_cast<SubstTemplateTemplateParmPackStorage *>(this) ; + return Bits.Kind == SubstTemplateTemplateParmPack + ? reinterpret_cast<SubstTemplateTemplateParmPackStorage *>(this) + : 0; } }; @@ -82,8 +94,8 @@ public: class OverloadedTemplateStorage : public UncommonTemplateNameStorage { friend class ASTContext; - OverloadedTemplateStorage(unsigned Size) - : UncommonTemplateNameStorage(Size, true) { } + OverloadedTemplateStorage(unsigned size) + : UncommonTemplateNameStorage(Overloaded, size) { } NamedDecl **getStorage() { return reinterpret_cast<NamedDecl **>(this + 1); @@ -98,8 +110,7 @@ public: iterator begin() const { return getStorage(); } iterator end() const { return getStorage() + size(); } }; - - + /// \brief A structure for storing an already-substituted template template /// parameter pack. /// @@ -109,16 +120,14 @@ public: class SubstTemplateTemplateParmPackStorage : public UncommonTemplateNameStorage, public llvm::FoldingSetNode { - ASTContext &Context; TemplateTemplateParmDecl *Parameter; const TemplateArgument *Arguments; public: - SubstTemplateTemplateParmPackStorage(ASTContext &Context, - TemplateTemplateParmDecl *Parameter, + SubstTemplateTemplateParmPackStorage(TemplateTemplateParmDecl *Parameter, unsigned Size, const TemplateArgument *Arguments) - : UncommonTemplateNameStorage(Size, false), Context(Context), + : UncommonTemplateNameStorage(SubstTemplateTemplateParmPack, Size), Parameter(Parameter), Arguments(Arguments) { } /// \brief Retrieve the template template parameter pack being substituted. @@ -130,9 +139,10 @@ public: /// parameter was substituted. TemplateArgument getArgumentPack() const; - void Profile(llvm::FoldingSetNodeID &ID); + void Profile(llvm::FoldingSetNodeID &ID, ASTContext &Context); - static void Profile(llvm::FoldingSetNodeID &ID, ASTContext &Context, + static void Profile(llvm::FoldingSetNodeID &ID, + ASTContext &Context, TemplateTemplateParmDecl *Parameter, const TemplateArgument &ArgPack); }; @@ -189,6 +199,9 @@ public: /// \brief A dependent template name that has not been resolved to a /// template (or set of templates). DependentTemplate, + /// \brief A template template parameter that has been substituted + /// for some other template name. + SubstTemplateTemplateParm, /// \brief A template template parameter pack that has been substituted for /// a template template argument pack, but has not yet been expanded into /// individual arguments. @@ -199,6 +212,7 @@ public: explicit TemplateName(TemplateDecl *Template) : Storage(Template) { } explicit TemplateName(OverloadedTemplateStorage *Storage) : Storage(Storage) { } + explicit TemplateName(SubstTemplateTemplateParmStorage *Storage); explicit TemplateName(SubstTemplateTemplateParmPackStorage *Storage) : Storage(Storage) { } explicit TemplateName(QualifiedTemplateName *Qual) : Storage(Qual) { } @@ -234,6 +248,19 @@ public: return 0; } + /// \brief Retrieve the substituted template template parameter, if + /// known. + /// + /// \returns The storage for the substituted template template parameter, + /// if known. Otherwise, returns NULL. + SubstTemplateTemplateParmStorage *getAsSubstTemplateTemplateParm() const { + if (UncommonTemplateNameStorage *uncommon = + Storage.dyn_cast<UncommonTemplateNameStorage *>()) + return uncommon->getAsSubstTemplateTemplateParm(); + + return 0; + } + /// \brief Retrieve the substituted template template parameter pack, if /// known. /// @@ -260,9 +287,15 @@ public: return Storage.dyn_cast<DependentTemplateName *>(); } + TemplateName getUnderlying() const; + /// \brief Determines whether this is a dependent template name. bool isDependent() const; + /// \brief Determines whether this is a template name that somehow + /// depends on a template parameter. + bool isInstantiationDependent() const; + /// \brief Determines whether this template name contains an /// unexpanded parameter pack (for C++0x variadic templates). bool containsUnexpandedParameterPack() const; @@ -300,6 +333,41 @@ public: const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB, TemplateName N); +/// \brief A structure for storing the information associated with a +/// substituted template template parameter. +class SubstTemplateTemplateParmStorage + : public UncommonTemplateNameStorage, public llvm::FoldingSetNode { + friend class ASTContext; + + TemplateTemplateParmDecl *Parameter; + TemplateName Replacement; + + SubstTemplateTemplateParmStorage(TemplateTemplateParmDecl *parameter, + TemplateName replacement) + : UncommonTemplateNameStorage(SubstTemplateTemplateParm, 0), + Parameter(parameter), Replacement(replacement) {} + +public: + TemplateTemplateParmDecl *getParameter() const { return Parameter; } + TemplateName getReplacement() const { return Replacement; } + + void Profile(llvm::FoldingSetNodeID &ID); + + static void Profile(llvm::FoldingSetNodeID &ID, + TemplateTemplateParmDecl *parameter, + TemplateName replacement); +}; + +inline TemplateName::TemplateName(SubstTemplateTemplateParmStorage *Storage) + : Storage(Storage) { } + +inline TemplateName TemplateName::getUnderlying() const { + if (SubstTemplateTemplateParmStorage *subst + = getAsSubstTemplateTemplateParm()) + return subst->getReplacement().getUnderlying(); + return *this; +} + /// \brief Represents a template name that was expressed as a /// qualified name. /// diff --git a/include/clang/AST/Type.h b/include/clang/AST/Type.h index 77633831ff27..ef0dbdae037c 100644 --- a/include/clang/AST/Type.h +++ b/include/clang/AST/Type.h @@ -126,6 +126,28 @@ public: Strong }; + enum ObjCLifetime { + /// There is no lifetime qualification on this type. + OCL_None, + + /// This object can be modified without requiring retains or + /// releases. + OCL_ExplicitNone, + + /// Assigning into this object requires the old value to be + /// released and the new value to be retained. The timing of the + /// release of the old value is inexact: it may be moved to + /// immediately after the last known point where the value is + /// live. + OCL_Strong, + + /// Reading or writing from this object requires a barrier call. + OCL_Weak, + + /// Assigning into this object requires a lifetime extension. + OCL_Autoreleasing + }; + enum { /// The maximum supported address space number. /// 24 bits should be enough for anyone. @@ -218,7 +240,37 @@ public: qs.removeObjCGCAttr(); return qs; } + Qualifiers withoutObjCGLifetime() const { + Qualifiers qs = *this; + qs.removeObjCLifetime(); + return qs; + } + + bool hasObjCLifetime() const { return Mask & LifetimeMask; } + ObjCLifetime getObjCLifetime() const { + return ObjCLifetime((Mask & LifetimeMask) >> LifetimeShift); + } + void setObjCLifetime(ObjCLifetime type) { + Mask = (Mask & ~LifetimeMask) | (type << LifetimeShift); + } + void removeObjCLifetime() { setObjCLifetime(OCL_None); } + void addObjCLifetime(ObjCLifetime type) { + assert(type); + setObjCLifetime(type); + } + + /// True if the lifetime is neither None or ExplicitNone. + bool hasNonTrivialObjCLifetime() const { + ObjCLifetime lifetime = getObjCLifetime(); + return (lifetime > OCL_ExplicitNone); + } + /// True if the lifetime is either strong or weak. + bool hasStrongOrWeakObjCLifetime() const { + ObjCLifetime lifetime = getObjCLifetime(); + return (lifetime == OCL_Strong || lifetime == OCL_Weak); + } + bool hasAddressSpace() const { return Mask & AddressSpaceMask; } unsigned getAddressSpace() const { return Mask >> AddressSpaceShift; } void setAddressSpace(unsigned space) { @@ -277,6 +329,8 @@ public: addAddressSpace(Q.getAddressSpace()); if (Q.hasObjCGCAttr()) addObjCGCAttr(Q.getObjCGCAttr()); + if (Q.hasObjCLifetime()) + addObjCLifetime(Q.getObjCLifetime()); } } @@ -287,6 +341,8 @@ public: !hasAddressSpace() || !qs.hasAddressSpace()); assert(getObjCGCAttr() == qs.getObjCGCAttr() || !hasObjCGCAttr() || !qs.hasObjCGCAttr()); + assert(getObjCLifetime() == qs.getObjCLifetime() || + !hasObjCLifetime() || !qs.hasObjCLifetime()); Mask |= qs.Mask; } @@ -301,10 +357,30 @@ public: // changed. (getObjCGCAttr() == other.getObjCGCAttr() || !hasObjCGCAttr() || !other.hasObjCGCAttr()) && + // ObjC lifetime qualifiers must match exactly. + getObjCLifetime() == other.getObjCLifetime() && // CVR qualifiers may subset. (((Mask & CVRMask) | (other.Mask & CVRMask)) == (Mask & CVRMask)); } + /// \brief Determines if these qualifiers compatibly include another set of + /// qualifiers from the narrow perspective of Objective-C ARC lifetime. + /// + /// One set of Objective-C lifetime qualifiers compatibly includes the other + /// if the lifetime qualifiers match, or if both are non-__weak and the + /// including set also contains the 'const' qualifier. + bool compatiblyIncludesObjCLifetime(Qualifiers other) const { + if (getObjCLifetime() == other.getObjCLifetime()) + return true; + + if (getObjCLifetime() == OCL_Weak || other.getObjCLifetime() == OCL_Weak) + return false; + + return hasConst(); + } + + bool isSupersetOf(Qualifiers Other) const; + /// \brief Determine whether this set of qualifiers is a strict superset of /// another set of qualifiers, not considering qualifier compatibility. bool isStrictSupersetOf(Qualifiers Other) const; @@ -351,14 +427,16 @@ public: private: - // bits: |0 1 2|3 .. 4|5 .. 31| - // |C R V|GCAttr|AddrSpace| + // bits: |0 1 2|3 .. 4|5 .. 7|8 ... 31| + // |C R V|GCAttr|Lifetime|AddressSpace| uint32_t Mask; static const uint32_t GCAttrMask = 0x18; static const uint32_t GCAttrShift = 3; - static const uint32_t AddressSpaceMask = ~(CVRMask | GCAttrMask); - static const uint32_t AddressSpaceShift = 5; + static const uint32_t LifetimeMask = 0xE0; + static const uint32_t LifetimeShift = 5; + static const uint32_t AddressSpaceMask = ~(CVRMask|GCAttrMask|LifetimeMask); + static const uint32_t AddressSpaceShift = 8; }; /// CallingConv - Specifies the calling convention that a function uses. @@ -527,6 +605,23 @@ public: return QualType::isConstant(*this, Ctx); } + /// \brief Determine whether this is a Plain Old Data (POD) type (C++ 3.9p10). + bool isPODType(ASTContext &Context) const; + + /// isCXX11PODType() - Return true if this is a POD type according to the + /// more relaxed rules of the C++11 standard, regardless of the current + /// compilation's language. + /// (C++0x [basic.types]p9) + bool isCXX11PODType(ASTContext &Context) const; + + /// isTrivialType - Return true if this is a trivial type + /// (C++0x [basic.types]p9) + bool isTrivialType(ASTContext &Context) const; + + /// isTriviallyCopyableType - Return true if this is a trivially + /// copyable type (C++0x [basic.types]p9) + bool isTriviallyCopyableType(ASTContext &Context) const; + // Don't promise in the API that anything besides 'const' can be // easily added. @@ -546,6 +641,10 @@ public: return withFastQualifiers(Qualifiers::Volatile); } + QualType withCVRQualifiers(unsigned CVR) const { + return withFastQualifiers(CVR); + } + void addFastQualifiers(unsigned TQs) { assert(!(TQs & ~Qualifiers::FastMask) && "non-fast qualifier bits set in mask!"); @@ -658,6 +757,13 @@ public: return getSplitDesugaredType(*this); } + /// \brief Return the specified type with one level of "sugar" removed from + /// the type. + /// + /// This routine takes off the first typedef, typeof, etc. If the outer level + /// of the type is already concrete, it returns it unmodified. + QualType getSingleStepDesugaredType(const ASTContext &Context) const; + /// IgnoreParens - Returns the specified type after dropping any /// outer-level parentheses. QualType IgnoreParens() const { @@ -709,7 +815,7 @@ public: /// getAddressSpace - Return the address space of this type. inline unsigned getAddressSpace() const; - /// GCAttrTypesAttr - Returns gc attribute of this type. + /// getObjCGCAttr - Returns gc attribute of this type. inline Qualifiers::GC getObjCGCAttr() const; /// isObjCGCWeak true when Type is objc's weak. @@ -722,9 +828,24 @@ public: return getObjCGCAttr() == Qualifiers::Strong; } + /// getObjCLifetime - Returns lifetime attribute of this type. + Qualifiers::ObjCLifetime getObjCLifetime() const { + return getQualifiers().getObjCLifetime(); + } + + bool hasNonTrivialObjCLifetime() const { + return getQualifiers().hasNonTrivialObjCLifetime(); + } + + bool hasStrongOrWeakObjCLifetime() const { + return getQualifiers().hasStrongOrWeakObjCLifetime(); + } + enum DestructionKind { DK_none, - DK_cxx_destructor + DK_cxx_destructor, + DK_objc_strong_lifetime, + DK_objc_weak_lifetime }; /// isDestructedType - nonzero if objects of this type require @@ -735,6 +856,21 @@ public: return isDestructedTypeImpl(*this); } + /// \brief Determine whether expressions of the given type are forbidden + /// from being lvalues in C. + /// + /// The expression types that are forbidden to be lvalues are: + /// - 'void', but not qualified void + /// - function types + /// + /// The exact rule here is C99 6.3.2.1: + /// An lvalue is an expression with an object type or an incomplete + /// type other than void. + bool isCForbiddenLValueType() const; + + /// \brief Determine whether this type has trivial copy-assignment semantics. + bool hasTrivialCopyAssignment(ASTContext &Context) const; + private: // These methods are implemented in a separate translation unit; // "static"-ize them to avoid creating temporary QualTypes in the @@ -849,6 +985,11 @@ public: bool hasObjCGCAttr() const { return Quals.hasObjCGCAttr(); } Qualifiers::GC getObjCGCAttr() const { return Quals.getObjCGCAttr(); } + bool hasObjCLifetime() const { return Quals.hasObjCLifetime(); } + Qualifiers::ObjCLifetime getObjCLifetime() const { + return Quals.getObjCLifetime(); + } + bool hasAddressSpace() const { return Quals.hasAddressSpace(); } unsigned getAddressSpace() const { return Quals.getAddressSpace(); } @@ -931,6 +1072,10 @@ private: /// subclasses can pack their bitfields into the same word. unsigned Dependent : 1; + /// \brief Whether this type somehow involves a template parameter, even + /// if the resolution of the type does not depend on a template parameter. + unsigned InstantiationDependent : 1; + /// \brief Whether this type is a variably-modified type (C99 6.7.5). unsigned VariablyModified : 1; @@ -968,7 +1113,7 @@ private: return CachedLocalOrUnnamed; } }; - enum { NumTypeBits = 17 }; + enum { NumTypeBits = 18 }; protected: // These classes allow subclasses to somewhat cleanly pack bitfields @@ -1111,12 +1256,14 @@ private: protected: // silence VC++ warning C4355: 'this' : used in base member initializer list Type *this_() { return this; } - Type(TypeClass tc, QualType canon, bool Dependent, bool VariablyModified, + Type(TypeClass tc, QualType canon, bool Dependent, + bool InstantiationDependent, bool VariablyModified, bool ContainsUnexpandedParameterPack) : ExtQualsTypeCommonBase(this, canon.isNull() ? QualType(this_(), 0) : canon) { TypeBits.TC = tc; TypeBits.Dependent = Dependent; + TypeBits.InstantiationDependent = Dependent || InstantiationDependent; TypeBits.VariablyModified = VariablyModified; TypeBits.ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; TypeBits.CacheValidAndVisibility = 0; @@ -1126,8 +1273,15 @@ protected: } friend class ASTContext; - void setDependent(bool D = true) { TypeBits.Dependent = D; } - void setVariablyModified(bool VM = true) { TypeBits.VariablyModified = VM; } + void setDependent(bool D = true) { + TypeBits.Dependent = D; + if (D) + TypeBits.InstantiationDependent = true; + } + void setInstantiationDependent(bool D = true) { + TypeBits.InstantiationDependent = D; } + void setVariablyModified(bool VM = true) { TypeBits.VariablyModified = VM; + } void setContainsUnexpandedParameterPack(bool PP = true) { TypeBits.ContainsUnexpandedParameterPack = PP; } @@ -1186,31 +1340,14 @@ public: return !isReferenceType() && !isFunctionType() && !isVoidType(); } - /// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10). - bool isPODType() const; - /// isLiteralType - Return true if this is a literal type /// (C++0x [basic.types]p10) bool isLiteralType() const; - /// isTrivialType - Return true if this is a trivial type - /// (C++0x [basic.types]p9) - bool isTrivialType() const; - - /// isTriviallyCopyableType - Return true if this is a trivially copyable type - /// (C++0x [basic.types]p9 - bool isTriviallyCopyableType() const; - /// \brief Test if this type is a standard-layout type. /// (C++0x [basic.type]p9) bool isStandardLayoutType() const; - /// isCXX11PODType() - Return true if this is a POD type according to the - /// more relaxed rules of the C++11 standard, regardless of the current - /// compilation's language. - /// (C++0x [basic.types]p9) - bool isCXX11PODType() const; - /// Helper methods to distinguish type categories. All type predicates /// operate on the canonical type, ignoring typedefs and qualifiers. @@ -1290,7 +1427,11 @@ public: bool isComplexIntegerType() const; // GCC _Complex integer type. bool isVectorType() const; // GCC vector type. bool isExtVectorType() const; // Extended vector type. - bool isObjCObjectPointerType() const; // Pointer to *any* ObjC object. + bool isObjCObjectPointerType() const; // pointer to ObjC object + bool isObjCRetainableType() const; // ObjC object or block pointer + bool isObjCLifetimeType() const; // (array of)* retainable type + bool isObjCIndirectLifetimeType() const; // (pointer to)* lifetime type + bool isObjCNSObjectType() const; // __attribute__((NSObject)) // FIXME: change this to 'raw' interface type, so we can used 'interface' type // for the common case. bool isObjCObjectType() const; // NSString or typeof(*(id)0) @@ -1302,9 +1443,19 @@ public: bool isObjCClassType() const; // Class bool isObjCSelType() const; // Class bool isObjCBuiltinType() const; // 'id' or 'Class' + bool isObjCARCBridgableType() const; + bool isCARCBridgableType() const; bool isTemplateTypeParmType() const; // C++ template type parameter bool isNullPtrType() const; // C++0x nullptr_t + /// Determines if this type, which must satisfy + /// isObjCLifetimeType(), is implicitly __unsafe_unretained rather + /// than implicitly __strong. + bool isObjCARCImplicitlyUnretainedType() const; + + /// Return the implicit lifetime for this type, which must not be dependent. + Qualifiers::ObjCLifetime getObjCARCImplicitLifetime() const; + enum ScalarTypeKind { STK_Pointer, STK_MemberPointer, @@ -1322,6 +1473,14 @@ public: /// (C++ [temp.dep.type]). bool isDependentType() const { return TypeBits.Dependent; } + /// \brief Determine whether this type is an instantiation-dependent type, + /// meaning that the type involves a template parameter (even if the + /// definition does not actually depend on the type substituted for that + /// template parameter). + bool isInstantiationDependentType() const { + return TypeBits.InstantiationDependent; + } + /// \brief Whether this type is a variably-modified type (C99 6.7.5). bool isVariablyModifiedType() const { return TypeBits.VariablyModified; } @@ -1336,6 +1495,8 @@ public: /// \brief Determine wither this type is a C++ elaborated-type-specifier. bool isElaboratedTypeSpecifier() const; + + bool canDecayToPointerType() const; /// hasPointerRepresentation - Whether this type is represented /// natively as a pointer; this includes pointers, references, block @@ -1480,6 +1641,7 @@ public: } CanQualType getCanonicalTypeUnqualified() const; // in CanonicalType.h void dump() const; + static bool classof(const Type *) { return true; } friend class ASTReader; @@ -1586,6 +1748,7 @@ public: public: BuiltinType(Kind K) : Type(Builtin, QualType(), /*Dependent=*/(K == Dependent), + /*InstantiationDependent=*/(K == Dependent), /*VariablyModified=*/false, /*Unexpanded paramter pack=*/false) { BuiltinTypeBits.Kind = K; @@ -1631,6 +1794,7 @@ class ComplexType : public Type, public llvm::FoldingSetNode { QualType ElementType; ComplexType(QualType Element, QualType CanonicalPtr) : Type(Complex, CanonicalPtr, Element->isDependentType(), + Element->isInstantiationDependentType(), Element->isVariablyModifiedType(), Element->containsUnexpandedParameterPack()), ElementType(Element) { @@ -1661,6 +1825,7 @@ class ParenType : public Type, public llvm::FoldingSetNode { ParenType(QualType InnerType, QualType CanonType) : Type(Paren, CanonType, InnerType->isDependentType(), + InnerType->isInstantiationDependentType(), InnerType->isVariablyModifiedType(), InnerType->containsUnexpandedParameterPack()), Inner(InnerType) { @@ -1692,6 +1857,7 @@ class PointerType : public Type, public llvm::FoldingSetNode { PointerType(QualType Pointee, QualType CanonicalPtr) : Type(Pointer, CanonicalPtr, Pointee->isDependentType(), + Pointee->isInstantiationDependentType(), Pointee->isVariablyModifiedType(), Pointee->containsUnexpandedParameterPack()), PointeeType(Pointee) { @@ -1724,6 +1890,7 @@ class BlockPointerType : public Type, public llvm::FoldingSetNode { QualType PointeeType; // Block is some kind of pointer type BlockPointerType(QualType Pointee, QualType CanonicalCls) : Type(BlockPointer, CanonicalCls, Pointee->isDependentType(), + Pointee->isInstantiationDependentType(), Pointee->isVariablyModifiedType(), Pointee->containsUnexpandedParameterPack()), PointeeType(Pointee) { @@ -1760,6 +1927,7 @@ protected: ReferenceType(TypeClass tc, QualType Referencee, QualType CanonicalRef, bool SpelledAsLValue) : Type(tc, CanonicalRef, Referencee->isDependentType(), + Referencee->isInstantiationDependentType(), Referencee->isVariablyModifiedType(), Referencee->containsUnexpandedParameterPack()), PointeeType(Referencee) @@ -1844,6 +2012,8 @@ class MemberPointerType : public Type, public llvm::FoldingSetNode { MemberPointerType(QualType Pointee, const Type *Cls, QualType CanonicalPtr) : Type(MemberPointer, CanonicalPtr, Cls->isDependentType() || Pointee->isDependentType(), + (Cls->isInstantiationDependentType() || + Pointee->isInstantiationDependentType()), Pointee->isVariablyModifiedType(), (Cls->containsUnexpandedParameterPack() || Pointee->containsUnexpandedParameterPack())), @@ -1911,6 +2081,7 @@ protected: ArraySizeModifier sm, unsigned tq, bool ContainsUnexpandedParameterPack) : Type(tc, can, et->isDependentType() || tc == DependentSizedArray, + et->isInstantiationDependentType() || tc == DependentSizedArray, (tc == VariableArray || et->isVariablyModifiedType()), ContainsUnexpandedParameterPack), ElementType(et) { @@ -2344,28 +2515,32 @@ class FunctionType : public Type { // you'll need to adjust both the Bits field below and // Type::FunctionTypeBitfields. - // | CC |noreturn|hasregparm|regparm - // |0 .. 2| 3 | 4 |5 .. 7 + // | CC |noreturn|produces|regparm| + // |0 .. 2| 3 | 4 | 5 .. 7| + // + // regparm is either 0 (no regparm attribute) or the regparm value+1. enum { CallConvMask = 0x7 }; enum { NoReturnMask = 0x8 }; - enum { HasRegParmMask = 0x10 }; - enum { RegParmMask = ~(CallConvMask | NoReturnMask), - RegParmOffset = 5 }; + enum { ProducesResultMask = 0x10 }; + enum { RegParmMask = ~(CallConvMask | NoReturnMask | ProducesResultMask), + RegParmOffset = 5 }; // Assumed to be the last field - unsigned char Bits; + uint16_t Bits; - ExtInfo(unsigned Bits) : Bits(static_cast<unsigned char>(Bits)) {} + ExtInfo(unsigned Bits) : Bits(static_cast<uint16_t>(Bits)) {} friend class FunctionType; public: // Constructor with no defaults. Use this when you know that you // have all the elements (when reading an AST file for example). - ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc) { + ExtInfo(bool noReturn, bool hasRegParm, unsigned regParm, CallingConv cc, + bool producesResult) { + assert((!hasRegParm || regParm < 7) && "Invalid regparm value"); Bits = ((unsigned) cc) | (noReturn ? NoReturnMask : 0) | - (hasRegParm ? HasRegParmMask : 0) | - (regParm << RegParmOffset); + (producesResult ? ProducesResultMask : 0) | + (hasRegParm ? ((regParm + 1) << RegParmOffset) : 0); } // Constructor with all defaults. Use when for example creating a @@ -2373,8 +2548,14 @@ class FunctionType : public Type { ExtInfo() : Bits(0) {} bool getNoReturn() const { return Bits & NoReturnMask; } - bool getHasRegParm() const { return Bits & HasRegParmMask; } - unsigned getRegParm() const { return Bits >> RegParmOffset; } + bool getProducesResult() const { return Bits & ProducesResultMask; } + bool getHasRegParm() const { return (Bits >> RegParmOffset) != 0; } + unsigned getRegParm() const { + unsigned RegParm = Bits >> RegParmOffset; + if (RegParm > 0) + --RegParm; + return RegParm; + } CallingConv getCC() const { return CallingConv(Bits & CallConvMask); } bool operator==(ExtInfo Other) const { @@ -2394,8 +2575,17 @@ class FunctionType : public Type { return ExtInfo(Bits & ~NoReturnMask); } + ExtInfo withProducesResult(bool producesResult) const { + if (producesResult) + return ExtInfo(Bits | ProducesResultMask); + else + return ExtInfo(Bits & ~ProducesResultMask); + } + ExtInfo withRegParm(unsigned RegParm) const { - return ExtInfo(HasRegParmMask | (Bits & ~RegParmMask) | (RegParm << RegParmOffset)); + assert(RegParm < 7 && "Invalid regparm value"); + return ExtInfo((Bits & ~RegParmMask) | + ((RegParm + 1) << RegParmOffset)); } ExtInfo withCallingConv(CallingConv cc) const { @@ -2411,9 +2601,10 @@ protected: FunctionType(TypeClass tc, QualType res, bool variadic, unsigned typeQuals, RefQualifierKind RefQualifier, QualType Canonical, bool Dependent, + bool InstantiationDependent, bool VariablyModified, bool ContainsUnexpandedParameterPack, ExtInfo Info) - : Type(tc, Canonical, Dependent, VariablyModified, + : Type(tc, Canonical, Dependent, InstantiationDependent, VariablyModified, ContainsUnexpandedParameterPack), ResultType(res) { FunctionTypeBits.ExtInfo = Info.Bits; @@ -2458,7 +2649,8 @@ public: class FunctionNoProtoType : public FunctionType, public llvm::FoldingSetNode { FunctionNoProtoType(QualType Result, QualType Canonical, ExtInfo Info) : FunctionType(FunctionNoProto, Result, false, 0, RQ_None, Canonical, - /*Dependent=*/false, Result->isVariablyModifiedType(), + /*Dependent=*/false, /*InstantiationDependent=*/false, + Result->isVariablyModifiedType(), /*ContainsUnexpandedParameterPack=*/false, Info) {} friend class ASTContext; // ASTContext creates these. @@ -2495,7 +2687,8 @@ public: struct ExtProtoInfo { ExtProtoInfo() : Variadic(false), ExceptionSpecType(EST_None), TypeQuals(0), - RefQualifier(RQ_None), NumExceptions(0), Exceptions(0), NoexceptExpr(0) {} + RefQualifier(RQ_None), NumExceptions(0), Exceptions(0), NoexceptExpr(0), + ConsumedArguments(0) {} FunctionType::ExtInfo ExtInfo; bool Variadic; @@ -2505,6 +2698,7 @@ public: unsigned NumExceptions; const QualType *Exceptions; Expr *NoexceptExpr; + const bool *ConsumedArguments; }; private: @@ -2523,7 +2717,7 @@ private: QualType canonical, const ExtProtoInfo &epi); /// NumArgs - The number of arguments this function has, not counting '...'. - unsigned NumArgs : 20; + unsigned NumArgs : 19; /// NumExceptions - The number of types in the exception spec, if any. unsigned NumExceptions : 9; @@ -2531,6 +2725,9 @@ private: /// ExceptionSpecType - The type of exception specification this function has. unsigned ExceptionSpecType : 3; + /// HasAnyConsumedArgs - Whether this function has any consumed arguments. + unsigned HasAnyConsumedArgs : 1; + /// ArgInfo - There is an variable size array after the class in memory that /// holds the argument types. @@ -2540,8 +2737,25 @@ private: /// NoexceptExpr - Instead of Exceptions, there may be a single Expr* pointing /// to the expression in the noexcept() specifier. + /// ConsumedArgs - A variable size array, following Exceptions + /// and of length NumArgs, holding flags indicating which arguments + /// are consumed. This only appears if HasAnyConsumedArgs is true. + friend class ASTContext; // ASTContext creates these. + const bool *getConsumedArgsBuffer() const { + assert(hasAnyConsumedArgs()); + + // Find the end of the exceptions. + Expr * const *eh_end = reinterpret_cast<Expr * const *>(arg_type_end()); + if (getExceptionSpecType() != EST_ComputedNoexcept) + eh_end += NumExceptions; + else + eh_end += 1; // NoexceptExpr + + return reinterpret_cast<const bool*>(eh_end); + } + public: unsigned getNumArgs() const { return NumArgs; } QualType getArgType(unsigned i) const { @@ -2562,6 +2776,8 @@ public: } else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { EPI.NoexceptExpr = getNoexceptExpr(); } + if (hasAnyConsumedArgs()) + EPI.ConsumedArguments = getConsumedArgsBuffer(); return EPI; } @@ -2647,6 +2863,16 @@ public: return exception_begin() + NumExceptions; } + bool hasAnyConsumedArgs() const { + return HasAnyConsumedArgs; + } + bool isArgConsumed(unsigned I) const { + assert(I < getNumArgs() && "argument index out of range!"); + if (hasAnyConsumedArgs()) + return getConsumedArgsBuffer()[I]; + return false; + } + bool isSugared() const { return false; } QualType desugar() const { return QualType(this, 0); } @@ -2670,7 +2896,7 @@ class UnresolvedUsingType : public Type { UnresolvedUsingTypenameDecl *Decl; UnresolvedUsingType(const UnresolvedUsingTypenameDecl *D) - : Type(UnresolvedUsing, QualType(), true, false, + : Type(UnresolvedUsing, QualType(), true, true, false, /*ContainsUnexpandedParameterPack=*/false), Decl(const_cast<UnresolvedUsingTypenameDecl*>(D)) {} friend class ASTContext; // ASTContext creates these. @@ -2700,7 +2926,9 @@ class TypedefType : public Type { TypedefNameDecl *Decl; protected: TypedefType(TypeClass tc, const TypedefNameDecl *D, QualType can) - : Type(tc, can, can->isDependentType(), can->isVariablyModifiedType(), + : Type(tc, can, can->isDependentType(), + can->isInstantiationDependentType(), + can->isVariablyModifiedType(), /*ContainsUnexpandedParameterPack=*/false), Decl(const_cast<TypedefNameDecl*>(D)) { assert(!isa<TypedefType>(can) && "Invalid canonical type"); @@ -2731,7 +2959,7 @@ public: QualType desugar() const; /// \brief Returns whether this type directly provides sugar. - bool isSugared() const { return true; } + bool isSugared() const; static bool classof(const Type *T) { return T->getTypeClass() == TypeOfExpr; } static bool classof(const TypeOfExprType *) { return true; } @@ -2751,9 +2979,6 @@ public: DependentTypeOfExprType(const ASTContext &Context, Expr *E) : TypeOfExprType(E), Context(Context) { } - bool isSugared() const { return false; } - QualType desugar() const { return QualType(this, 0); } - void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Context, getUnderlyingExpr()); } @@ -2766,7 +2991,9 @@ public: class TypeOfType : public Type { QualType TOType; TypeOfType(QualType T, QualType can) - : Type(TypeOf, can, T->isDependentType(), T->isVariablyModifiedType(), + : Type(TypeOf, can, T->isDependentType(), + T->isInstantiationDependentType(), + T->isVariablyModifiedType(), T->containsUnexpandedParameterPack()), TOType(T) { assert(!isa<TypedefType>(can) && "Invalid canonical type"); @@ -2802,10 +3029,10 @@ public: QualType getUnderlyingType() const { return UnderlyingType; } /// \brief Remove a single level of sugar. - QualType desugar() const { return getUnderlyingType(); } + QualType desugar() const; /// \brief Returns whether this type directly provides sugar. - bool isSugared() const { return !isDependentType(); } + bool isSugared() const; static bool classof(const Type *T) { return T->getTypeClass() == Decltype; } static bool classof(const DecltypeType *) { return true; } @@ -2823,9 +3050,6 @@ class DependentDecltypeType : public DecltypeType, public llvm::FoldingSetNode { public: DependentDecltypeType(const ASTContext &Context, Expr *E); - bool isSugared() const { return false; } - QualType desugar() const { return QualType(this, 0); } - void Profile(llvm::FoldingSetNodeID &ID) { Profile(ID, Context, getUnderlyingExpr()); } @@ -2971,6 +3195,7 @@ public: // Enumerated operand (string or keyword). attr_objc_gc, + attr_objc_ownership, attr_pcs, FirstEnumOperandKind = attr_objc_gc, @@ -2994,6 +3219,7 @@ private: AttributedType(QualType canon, Kind attrKind, QualType modified, QualType equivalent) : Type(Attributed, canon, canon->isDependentType(), + canon->isInstantiationDependentType(), canon->isVariablyModifiedType(), canon->containsUnexpandedParameterPack()), ModifiedType(modified), EquivalentType(equivalent) { @@ -3046,13 +3272,16 @@ class TemplateTypeParmType : public Type, public llvm::FoldingSetNode { /// Build a non-canonical type. TemplateTypeParmType(TemplateTypeParmDecl *TTPDecl, QualType Canon) : Type(TemplateTypeParm, Canon, /*Dependent=*/true, + /*InstantiationDependent=*/true, /*VariablyModified=*/false, Canon->containsUnexpandedParameterPack()), TTPDecl(TTPDecl) { } /// Build the canonical type. TemplateTypeParmType(unsigned D, unsigned I, bool PP) - : Type(TemplateTypeParm, QualType(this, 0), /*Dependent=*/true, + : Type(TemplateTypeParm, QualType(this, 0), + /*Dependent=*/true, + /*InstantiationDependent=*/true, /*VariablyModified=*/false, PP) { CanTTPTInfo.Depth = D; CanTTPTInfo.Index = I; @@ -3112,6 +3341,7 @@ class SubstTemplateTypeParmType : public Type, public llvm::FoldingSetNode { SubstTemplateTypeParmType(const TemplateTypeParmType *Param, QualType Canon) : Type(SubstTemplateTypeParm, Canon, Canon->isDependentType(), + Canon->isInstantiationDependentType(), Canon->isVariablyModifiedType(), Canon->containsUnexpandedParameterPack()), Replaced(Param) { } @@ -3211,6 +3441,7 @@ class AutoType : public Type, public llvm::FoldingSetNode { AutoType(QualType DeducedType) : Type(Auto, DeducedType.isNull() ? QualType(this, 0) : DeducedType, /*Dependent=*/DeducedType.isNull(), + /*InstantiationDependent=*/DeducedType.isNull(), /*VariablyModified=*/false, /*ContainsParameterPack=*/false) { assert((DeducedType.isNull() || !DeducedType->isDependentType()) && "deduced a dependent type for auto"); @@ -3244,28 +3475,35 @@ public: static bool classof(const AutoType *T) { return true; } }; -/// \brief Represents the type of a template specialization as written -/// in the source code. +/// \brief Represents a type template specialization; the template +/// must be a class template, a type alias template, or a template +/// template parameter. A template which cannot be resolved to one of +/// these, e.g. because it is written with a dependent scope +/// specifier, is instead represented as a +/// @c DependentTemplateSpecializationType. /// -/// Template specialization types represent the syntactic form of a -/// template-id that refers to a type, e.g., @c vector<int>. Some -/// template specialization types are syntactic sugar, whose canonical -/// type will point to some other type node that represents the -/// instantiation or class template specialization. For example, a -/// class template specialization type of @c vector<int> will refer to -/// a tag type for the instantiation -/// @c std::vector<int, std::allocator<int>>. +/// A non-dependent template specialization type is always "sugar", +/// typically for a @c RecordType. For example, a class template +/// specialization type of @c vector<int> will refer to a tag type for +/// the instantiation @c std::vector<int, std::allocator<int>> /// -/// Other template specialization types, for which the template name -/// is dependent, may be canonical types. These types are always -/// dependent. +/// Template specializations are dependent if either the template or +/// any of the template arguments are dependent, in which case the +/// type may also be canonical. /// -/// An instance of this type is followed by an array of TemplateArgument*s, -/// then, if the template specialization type is for a type alias template, -/// a QualType representing the non-canonical aliased type. +/// Instances of this type are allocated with a trailing array of +/// TemplateArguments, followed by a QualType representing the +/// non-canonical aliased type when the template is a type alias +/// template. class TemplateSpecializationType : public Type, public llvm::FoldingSetNode { - /// \brief The name of the template being specialized. + /// \brief The name of the template being specialized. This is + /// either a TemplateName::Template (in which case it is a + /// ClassTemplateDecl*, a TemplateTemplateParmDecl*, or a + /// TypeAliasTemplateDecl*), a + /// TemplateName::SubstTemplateTemplateParmPack, or a + /// TemplateName::SubstTemplateTemplateParm (in which case the + /// replacement must, recursively, be one of these). TemplateName Template; /// \brief - The number of template arguments named in this class @@ -3283,12 +3521,15 @@ public: /// \brief Determine whether any of the given template arguments are /// dependent. static bool anyDependentTemplateArguments(const TemplateArgument *Args, - unsigned NumArgs); + unsigned NumArgs, + bool &InstantiationDependent); static bool anyDependentTemplateArguments(const TemplateArgumentLoc *Args, - unsigned NumArgs); + unsigned NumArgs, + bool &InstantiationDependent); - static bool anyDependentTemplateArguments(const TemplateArgumentListInfo &); + static bool anyDependentTemplateArguments(const TemplateArgumentListInfo &, + bool &InstantiationDependent); /// \brief Print a template argument list, including the '<' and '>' /// enclosing the template arguments. @@ -3399,6 +3640,7 @@ class InjectedClassNameType : public Type { // interdependencies. InjectedClassNameType(CXXRecordDecl *D, QualType TST) : Type(InjectedClassName, QualType(), /*Dependent=*/true, + /*InstantiationDependent=*/true, /*VariablyModified=*/false, /*ContainsUnexpandedParameterPack=*/false), Decl(D), InjectedType(TST) { @@ -3461,9 +3703,10 @@ enum ElaboratedTypeKeyword { class TypeWithKeyword : public Type { protected: TypeWithKeyword(ElaboratedTypeKeyword Keyword, TypeClass tc, - QualType Canonical, bool Dependent, bool VariablyModified, + QualType Canonical, bool Dependent, + bool InstantiationDependent, bool VariablyModified, bool ContainsUnexpandedParameterPack) - : Type(tc, Canonical, Dependent, VariablyModified, + : Type(tc, Canonical, Dependent, InstantiationDependent, VariablyModified, ContainsUnexpandedParameterPack) { TypeWithKeywordBits.Keyword = Keyword; } @@ -3523,6 +3766,7 @@ class ElaboratedType : public TypeWithKeyword, public llvm::FoldingSetNode { QualType NamedType, QualType CanonType) : TypeWithKeyword(Keyword, Elaborated, CanonType, NamedType->isDependentType(), + NamedType->isInstantiationDependentType(), NamedType->isVariablyModifiedType(), NamedType->containsUnexpandedParameterPack()), NNS(NNS), NamedType(NamedType) { @@ -3585,6 +3829,7 @@ class DependentNameType : public TypeWithKeyword, public llvm::FoldingSetNode { DependentNameType(ElaboratedTypeKeyword Keyword, NestedNameSpecifier *NNS, const IdentifierInfo *Name, QualType CanonType) : TypeWithKeyword(Keyword, DependentName, CanonType, /*Dependent=*/true, + /*InstantiationDependent=*/true, /*VariablyModified=*/false, NNS->containsUnexpandedParameterPack()), NNS(NNS), Name(Name) { @@ -3738,6 +3983,7 @@ class PackExpansionType : public Type, public llvm::FoldingSetNode { PackExpansionType(QualType Pattern, QualType Canon, llvm::Optional<unsigned> NumExpansions) : Type(PackExpansion, Canon, /*Dependent=*/true, + /*InstantiationDependent=*/true, /*VariableModified=*/Pattern->isVariablyModifiedType(), /*ContainsUnexpandedParameterPack=*/false), Pattern(Pattern), @@ -3829,7 +4075,7 @@ protected: enum Nonce_ObjCInterface { Nonce_ObjCInterface }; ObjCObjectType(enum Nonce_ObjCInterface) - : Type(ObjCInterface, QualType(), false, false, false), + : Type(ObjCInterface, QualType(), false, false, false, false), BaseType(QualType(this_(), 0)) { ObjCObjectTypeBits.NumProtocols = 0; } @@ -3986,7 +4232,7 @@ class ObjCObjectPointerType : public Type, public llvm::FoldingSetNode { QualType PointeeType; ObjCObjectPointerType(QualType Canonical, QualType Pointee) - : Type(ObjCObjectPointer, Canonical, false, false, false), + : Type(ObjCObjectPointer, Canonical, false, false, false, false), PointeeType(Pointee) {} friend class ASTContext; // ASTContext creates these. @@ -4303,6 +4549,11 @@ inline QualType QualType::getNonReferenceType() const { return *this; } +inline bool QualType::isCForbiddenLValueType() const { + return ((getTypePtr()->isVoidType() && !hasQualifiers()) || + getTypePtr()->isFunctionType()); +} + /// \brief Tests whether the type is categorized as a fundamental type. /// /// \returns True for types specified in C++0x [basic.fundamental]. @@ -4480,6 +4731,11 @@ inline bool Type::isOverloadableType() const { return isDependentType() || isRecordType() || isEnumeralType(); } +/// \brief Determines whether this type can decay to a pointer type. +inline bool Type::canDecayToPointerType() const { + return isFunctionType() || isArrayType(); +} + inline bool Type::hasPointerRepresentation() const { return (isPointerType() || isReferenceType() || isBlockPointerType() || isObjCObjectPointerType() || isNullPtrType()); diff --git a/include/clang/Analysis/Analyses/UninitializedValues.h b/include/clang/Analysis/Analyses/UninitializedValues.h index b966f3a90fff..badb493a9df4 100644 --- a/include/clang/Analysis/Analyses/UninitializedValues.h +++ b/include/clang/Analysis/Analyses/UninitializedValues.h @@ -32,10 +32,16 @@ public: const VarDecl *vd, bool isAlwaysUninit) {} }; - + +struct UninitVariablesAnalysisStats { + unsigned NumVariablesAnalyzed; + unsigned NumBlockVisits; +}; + void runUninitializedVariablesAnalysis(const DeclContext &dc, const CFG &cfg, AnalysisContext &ac, - UninitVariablesHandler &handler); + UninitVariablesHandler &handler, + UninitVariablesAnalysisStats &stats); } #endif diff --git a/include/clang/Analysis/AnalysisContext.h b/include/clang/Analysis/AnalysisContext.h index 66c12a5384d4..6a1876e65900 100644 --- a/include/clang/Analysis/AnalysisContext.h +++ b/include/clang/Analysis/AnalysisContext.h @@ -107,6 +107,11 @@ public: void dumpCFG(); + /// \brief Returns true if we have built a CFG for this analysis context. + /// Note that this doesn't correspond to whether or not a valid CFG exists, it + /// corresponds to whether we *attempted* to build one. + bool isCFGBuilt() const { return builtCFG; } + ParentMap &getParentMap(); PseudoConstantAnalysis *getPseudoConstantAnalysis(); LiveVariables *getLiveVariables(); diff --git a/include/clang/Analysis/DomainSpecific/CocoaConventions.h b/include/clang/Analysis/DomainSpecific/CocoaConventions.h index 18e81fed79f8..5a4e06ff53e5 100644 --- a/include/clang/Analysis/DomainSpecific/CocoaConventions.h +++ b/include/clang/Analysis/DomainSpecific/CocoaConventions.h @@ -7,34 +7,45 @@ // //===----------------------------------------------------------------------===// // -// This file defines +// This file implements cocoa naming convention analysis. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_ANALYSIS_DS_COCOA #define LLVM_CLANG_ANALYSIS_DS_COCOA +#include "llvm/ADT/StringRef.h" #include "clang/AST/Type.h" namespace clang { + +class ObjCMethodDecl; + namespace ento { namespace cocoa { enum NamingConvention { NoConvention, CreateRule, InitRule }; - NamingConvention deriveNamingConvention(Selector S); + NamingConvention deriveNamingConvention(Selector S, const ObjCMethodDecl *MD); - static inline bool followsFundamentalRule(Selector S) { - return deriveNamingConvention(S) == CreateRule; + static inline bool followsFundamentalRule(Selector S, + const ObjCMethodDecl *MD) { + return deriveNamingConvention(S, MD) == CreateRule; } bool isRefType(QualType RetTy, llvm::StringRef Prefix, llvm::StringRef Name = llvm::StringRef()); - + + bool isCocoaObjectRef(QualType T); + +} + +namespace coreFoundation { bool isCFObjectRef(QualType T); - bool isCocoaObjectRef(QualType T); + bool followsCreateRule(llvm::StringRef functionName); +} -}}} +}} // end: "clang:ento" #endif diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td index e4c6722e8378..e64dc6a2ade0 100644 --- a/include/clang/Basic/Attr.td +++ b/include/clang/Basic/Attr.td @@ -400,6 +400,11 @@ def ObjCNSObject : InheritableAttr { let Spellings = ["NSObject"]; } +def ObjCPreciseLifetime : Attr { + let Spellings = ["objc_precise_lifetime"]; + let Subjects = [Var]; +} + def Overloadable : Attr { let Spellings = ["overloadable"]; } @@ -479,6 +484,10 @@ def Unavailable : InheritableAttr { let Args = [StringArgument<"Message">]; } +def ArcWeakrefUnavailable : InheritableAttr { + let Spellings = ["objc_arc_weak_reference_unavailable"]; +} + def Unused : InheritableAttr { let Spellings = ["unused"]; } diff --git a/include/clang/Basic/Builtins.def b/include/clang/Basic/Builtins.def index 9a4c768dc649..a3cc6156238e 100644 --- a/include/clang/Basic/Builtins.def +++ b/include/clang/Basic/Builtins.def @@ -604,6 +604,8 @@ BUILTIN(__builtin_rindex, "c*cC*i", "Fn") // Microsoft builtins. BUILTIN(__assume, "vb", "n") BUILTIN(__noop, "v.", "n") +BUILTIN(__debugbreak, "v", "n") + // C99 library functions // C99 stdlib.h @@ -727,6 +729,10 @@ LIBBUILTIN(cos, "dd", "fe", "math.h", ALL_LANGUAGES) LIBBUILTIN(cosl, "LdLd", "fe", "math.h", ALL_LANGUAGES) LIBBUILTIN(cosf, "ff", "fe", "math.h", ALL_LANGUAGES) +LIBBUILTIN(fma, "dddd", "fc", "math.h", ALL_LANGUAGES) +LIBBUILTIN(fmal, "LdLdLdLd", "fc", "math.h", ALL_LANGUAGES) +LIBBUILTIN(fmaf, "ffff", "fc", "math.h", ALL_LANGUAGES) + // Blocks runtime Builtin math library functions LIBBUILTIN(_Block_object_assign, "vv*vC*iC", "f", "Blocks.h", ALL_LANGUAGES) LIBBUILTIN(_Block_object_dispose, "vvC*iC", "f", "Blocks.h", ALL_LANGUAGES) diff --git a/include/clang/Basic/Builtins.h b/include/clang/Basic/Builtins.h index 0d17e03d8a52..7469e144c150 100644 --- a/include/clang/Basic/Builtins.h +++ b/include/clang/Basic/Builtins.h @@ -50,7 +50,6 @@ enum ID { struct Info { const char *Name, *Type, *Attributes, *HeaderName; LanguageID builtin_lang; - bool Suppressed; bool operator==(const Info &RHS) const { return !strcmp(Name, RHS.Name) && diff --git a/include/clang/Basic/BuiltinsX86.def b/include/clang/Basic/BuiltinsX86.def index 6ef667db7beb..6bd901469cee 100644 --- a/include/clang/Basic/BuiltinsX86.def +++ b/include/clang/Basic/BuiltinsX86.def @@ -26,6 +26,7 @@ // 3DNow! // +BUILTIN(__builtin_ia32_femms, "v", "") BUILTIN(__builtin_ia32_pavgusb, "V8cV8cV8c", "nc") BUILTIN(__builtin_ia32_pf2id, "V2iV2f", "nc") BUILTIN(__builtin_ia32_pfacc, "V2fV2fV2f", "nc") @@ -47,7 +48,7 @@ BUILTIN(__builtin_ia32_pfsub, "V2fV2fV2f", "nc") BUILTIN(__builtin_ia32_pfsubr, "V2fV2fV2f", "nc") BUILTIN(__builtin_ia32_pi2fd, "V2fV2i", "nc") BUILTIN(__builtin_ia32_pmulhrw, "V4sV4sV4s", "nc") -// 3DNow! Extensions. +// 3DNow! Extensions (3dnowa). BUILTIN(__builtin_ia32_pf2iw, "V2iV2f", "nc") BUILTIN(__builtin_ia32_pfnacc, "V2fV2fV2f", "nc") BUILTIN(__builtin_ia32_pfpnacc, "V2fV2fV2f", "nc") @@ -57,15 +58,13 @@ BUILTIN(__builtin_ia32_pswapdsi, "V2iV2i", "nc") // MMX // -// FIXME: All MMX instructions will be generated via builtins. Any MMX vector +// All MMX instructions will be generated via builtins. Any MMX vector // types (<1 x i64>, <2 x i32>, etc.) that aren't used by these builtins will be // expanded by the back-end. BUILTIN(__builtin_ia32_emms, "v", "") -BUILTIN(__builtin_ia32_femms, "v", "") BUILTIN(__builtin_ia32_paddb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_paddw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_paddd, "V2iV2iV2i", "") -BUILTIN(__builtin_ia32_paddq, "V1LLiV1LLiV1LLi", "") BUILTIN(__builtin_ia32_paddsb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_paddsw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_paddusb, "V8cV8cV8c", "") @@ -73,27 +72,17 @@ BUILTIN(__builtin_ia32_paddusw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_psubb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_psubw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_psubd, "V2iV2iV2i", "") -BUILTIN(__builtin_ia32_psubq, "V1LLiV1LLiV1LLi", "") BUILTIN(__builtin_ia32_psubsb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_psubsw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_psubusb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_psubusw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_pmulhw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_pmullw, "V4sV4sV4s", "") -BUILTIN(__builtin_ia32_pmulhuw, "V4sV4sV4s", "") -BUILTIN(__builtin_ia32_pmuludq, "V1LLiV2iV2i", "") BUILTIN(__builtin_ia32_pmaddwd, "V2iV4sV4s", "") BUILTIN(__builtin_ia32_pand, "V1LLiV1LLiV1LLi", "") BUILTIN(__builtin_ia32_pandn, "V1LLiV1LLiV1LLi", "") BUILTIN(__builtin_ia32_por, "V1LLiV1LLiV1LLi", "") BUILTIN(__builtin_ia32_pxor, "V1LLiV1LLiV1LLi", "") -BUILTIN(__builtin_ia32_pavgb, "V8cV8cV8c", "") -BUILTIN(__builtin_ia32_pavgw, "V4sV4sV4s", "") -BUILTIN(__builtin_ia32_pmaxub, "V8cV8cV8c", "") -BUILTIN(__builtin_ia32_pmaxsw, "V4sV4sV4s", "") -BUILTIN(__builtin_ia32_pminub, "V8cV8cV8c", "") -BUILTIN(__builtin_ia32_pminsw, "V4sV4sV4s", "") -BUILTIN(__builtin_ia32_psadbw, "V4sV8cV8c", "") BUILTIN(__builtin_ia32_psllw, "V4sV4sV1LLi", "") BUILTIN(__builtin_ia32_pslld, "V2iV2iV1LLi", "") BUILTIN(__builtin_ia32_psllq, "V1LLiV1LLiV1LLi", "") @@ -113,7 +102,6 @@ BUILTIN(__builtin_ia32_psradi, "V2iV2ii", "") BUILTIN(__builtin_ia32_packsswb, "V8cV4sV4s", "") BUILTIN(__builtin_ia32_packssdw, "V4sV2iV2i", "") BUILTIN(__builtin_ia32_packuswb, "V8cV4sV4s", "") -BUILTIN(__builtin_ia32_pshufw, "V4sV4sIc", "") BUILTIN(__builtin_ia32_punpckhbw, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_punpckhwd, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_punpckhdq, "V2iV2iV2i", "") @@ -127,14 +115,53 @@ BUILTIN(__builtin_ia32_pcmpgtb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_pcmpgtw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_pcmpgtd, "V2iV2iV2i", "") BUILTIN(__builtin_ia32_maskmovq, "vV8cV8cc*", "") -BUILTIN(__builtin_ia32_pmovmskb, "iV8c", "") BUILTIN(__builtin_ia32_movntq, "vV1LLi*V1LLi", "") -BUILTIN(__builtin_ia32_palignr, "V8cV8cV8cIc", "") BUILTIN(__builtin_ia32_vec_init_v2si, "V2iii", "") BUILTIN(__builtin_ia32_vec_init_v4hi, "V4sssss", "") BUILTIN(__builtin_ia32_vec_init_v8qi, "V8ccccccccc", "") BUILTIN(__builtin_ia32_vec_ext_v2si, "iV2ii", "") +// MMX2 (MMX+SSE) intrinsics +BUILTIN(__builtin_ia32_cvtpi2ps, "V4fV4fV2i", "") +BUILTIN(__builtin_ia32_cvtps2pi, "V2iV4f", "") +BUILTIN(__builtin_ia32_cvttps2pi, "V2iV4f", "") +BUILTIN(__builtin_ia32_pavgb, "V8cV8cV8c", "") +BUILTIN(__builtin_ia32_pavgw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_pmaxsw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_pmaxub, "V8cV8cV8c", "") +BUILTIN(__builtin_ia32_pminsw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_pminub, "V8cV8cV8c", "") +BUILTIN(__builtin_ia32_pmovmskb, "iV8c", "") +BUILTIN(__builtin_ia32_pmulhuw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_psadbw, "V4sV8cV8c", "") +BUILTIN(__builtin_ia32_pshufw, "V4sV4sIc", "") + +// MMX+SSE2 +BUILTIN(__builtin_ia32_cvtpd2pi, "V2iV2d", "") +BUILTIN(__builtin_ia32_cvtpi2pd, "V2dV2i", "") +BUILTIN(__builtin_ia32_cvttpd2pi, "V2iV2d", "") +BUILTIN(__builtin_ia32_paddq, "V1LLiV1LLiV1LLi", "") +BUILTIN(__builtin_ia32_pmuludq, "V1LLiV2iV2i", "") +BUILTIN(__builtin_ia32_psubq, "V1LLiV1LLiV1LLi", "") + +// MMX+SSSE3 +BUILTIN(__builtin_ia32_pabsb, "V8cV8c", "") +BUILTIN(__builtin_ia32_pabsd, "V2iV2i", "") +BUILTIN(__builtin_ia32_pabsw, "V4sV4s", "") +BUILTIN(__builtin_ia32_palignr, "V8cV8cV8cIc", "") +BUILTIN(__builtin_ia32_phaddd, "V2iV2iV2i", "") +BUILTIN(__builtin_ia32_phaddsw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_phaddw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_phsubd, "V2iV2iV2i", "") +BUILTIN(__builtin_ia32_phsubsw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_phsubw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_pmaddubsw, "V8cV8cV8c", "") +BUILTIN(__builtin_ia32_pmulhrsw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_pshufb, "V8cV8cV8c", "") +BUILTIN(__builtin_ia32_psignw, "V4sV4sV4s", "") +BUILTIN(__builtin_ia32_psignb, "V8cV8cV8c", "") +BUILTIN(__builtin_ia32_psignd, "V2iV2iV2i", "") + // SSE intrinsics. BUILTIN(__builtin_ia32_comieq, "iV4fV4f", "") BUILTIN(__builtin_ia32_comilt, "iV4fV4f", "") @@ -204,42 +231,24 @@ BUILTIN(__builtin_ia32_haddpd, "V2dV2dV2d", "") BUILTIN(__builtin_ia32_hsubps, "V4fV4fV4f", "") BUILTIN(__builtin_ia32_hsubpd, "V2dV2dV2d", "") BUILTIN(__builtin_ia32_phaddw128, "V8sV8sV8s", "") -BUILTIN(__builtin_ia32_phaddw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_phaddd128, "V4iV4iV4i", "") -BUILTIN(__builtin_ia32_phaddd, "V2iV2iV2i", "") BUILTIN(__builtin_ia32_phaddsw128, "V8sV8sV8s", "") -BUILTIN(__builtin_ia32_phaddsw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_phsubw128, "V8sV8sV8s", "") -BUILTIN(__builtin_ia32_phsubw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_phsubd128, "V4iV4iV4i", "") -BUILTIN(__builtin_ia32_phsubd, "V2iV2iV2i", "") BUILTIN(__builtin_ia32_phsubsw128, "V8sV8sV8s", "") -BUILTIN(__builtin_ia32_phsubsw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_pmaddubsw128, "V16cV16cV16c", "") -BUILTIN(__builtin_ia32_pmaddubsw, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_pmulhrsw128, "V8sV8sV8s", "") -BUILTIN(__builtin_ia32_pmulhrsw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_pshufb128, "V16cV16cV16c", "") -BUILTIN(__builtin_ia32_pshufb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_psignb128, "V16cV16cV16c", "") -BUILTIN(__builtin_ia32_psignb, "V8cV8cV8c", "") BUILTIN(__builtin_ia32_psignw128, "V8sV8sV8s", "") -BUILTIN(__builtin_ia32_psignw, "V4sV4sV4s", "") BUILTIN(__builtin_ia32_psignd128, "V4iV4iV4i", "") -BUILTIN(__builtin_ia32_psignd, "V2iV2iV2i", "") BUILTIN(__builtin_ia32_pabsb128, "V16cV16c", "") -BUILTIN(__builtin_ia32_pabsb, "V8cV8c", "") BUILTIN(__builtin_ia32_pabsw128, "V8sV8s", "") -BUILTIN(__builtin_ia32_pabsw, "V4sV4s", "") BUILTIN(__builtin_ia32_pabsd128, "V4iV4i", "") -BUILTIN(__builtin_ia32_pabsd, "V2iV2i", "") BUILTIN(__builtin_ia32_ldmxcsr, "vUi", "") BUILTIN(__builtin_ia32_stmxcsr, "Ui", "") -BUILTIN(__builtin_ia32_cvtpi2ps, "V4fV4fV2i", "") -BUILTIN(__builtin_ia32_cvtps2pi, "V2iV4f", "") BUILTIN(__builtin_ia32_cvtss2si, "iV4f", "") BUILTIN(__builtin_ia32_cvtss2si64, "LLiV4f", "") -BUILTIN(__builtin_ia32_cvttps2pi, "V2iV4f", "") BUILTIN(__builtin_ia32_storeups, "vf*V4f", "") BUILTIN(__builtin_ia32_storehps, "vV2i*V4f", "") BUILTIN(__builtin_ia32_storelps, "vV2i*V4f", "") @@ -265,11 +274,8 @@ BUILTIN(__builtin_ia32_sqrtsd, "V2dV2d", "") BUILTIN(__builtin_ia32_cvtdq2pd, "V2dV4i", "") BUILTIN(__builtin_ia32_cvtdq2ps, "V4fV4i", "") BUILTIN(__builtin_ia32_cvtpd2dq, "V2LLiV2d", "") -BUILTIN(__builtin_ia32_cvtpd2pi, "V2iV2d", "") BUILTIN(__builtin_ia32_cvtpd2ps, "V4fV2d", "") BUILTIN(__builtin_ia32_cvttpd2dq, "V4iV2d", "") -BUILTIN(__builtin_ia32_cvttpd2pi, "V2iV2d", "") -BUILTIN(__builtin_ia32_cvtpi2pd, "V2dV2i", "") BUILTIN(__builtin_ia32_cvtsd2si, "iV2d", "") BUILTIN(__builtin_ia32_cvtsd2si64, "LLiV2d", "") BUILTIN(__builtin_ia32_cvtps2dq, "V4iV4f", "") diff --git a/include/clang/Basic/DelayedCleanupPool.h b/include/clang/Basic/DelayedCleanupPool.h new file mode 100644 index 000000000000..843205f7b011 --- /dev/null +++ b/include/clang/Basic/DelayedCleanupPool.h @@ -0,0 +1,109 @@ +//=== DelayedCleanupPool.h - Delayed Clean-up Pool Implementation *- C++ -*===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a facility to delay calling cleanup methods until specific +// points. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_DELAYEDCLEANUPPOOL_H +#define LLVM_CLANG_BASIC_DELAYEDCLEANUPPOOL_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang { + +/// \brief Gathers pairs of pointer-to-object/pointer-to-cleanup-function +/// allowing the cleanup functions to get called (with the pointer as parameter) +/// at specific points. +/// +/// The use case is to simplify clean-up of certain resources that, while their +/// lifetime is well-known and restricted, cleaning them up manually is easy to +/// miss and cause a leak. +/// +/// The same pointer can be added multiple times; its clean-up function will +/// only be called once. +class DelayedCleanupPool { +public: + typedef void (*CleanupFn)(void *ptr); + + /// \brief Adds a pointer and its associated cleanup function to be called + /// at a later point. + /// + /// \returns false if the pointer is already added, true otherwise. + bool delayCleanup(void *ptr, CleanupFn fn) { + assert(ptr && "Expected valid pointer to object"); + assert(fn && "Expected valid pointer to function"); + + CleanupFn &mapFn = Ptrs[ptr]; + assert((!mapFn || mapFn == fn) && + "Adding a pointer with different cleanup function!"); + + if (!mapFn) { + mapFn = fn; + Cleanups.push_back(std::make_pair(ptr, fn)); + return true; + } + + return false; + } + + template <typename T> + bool delayDelete(T *ptr) { + return delayCleanup(ptr, cleanupWithDelete<T>); + } + + template <typename T, void (T::*Fn)()> + bool delayMemberFunc(T *ptr) { + return delayCleanup(ptr, cleanupWithMemberFunc<T, Fn>); + } + + void doCleanup() { + for (llvm::SmallVector<std::pair<void *, CleanupFn>, 8>::reverse_iterator + I = Cleanups.rbegin(), E = Cleanups.rend(); I != E; ++I) + I->second(I->first); + Cleanups.clear(); + Ptrs.clear(); + } + + ~DelayedCleanupPool() { + doCleanup(); + } + +private: + llvm::DenseMap<void *, CleanupFn> Ptrs; + llvm::SmallVector<std::pair<void *, CleanupFn>, 8> Cleanups; + + template <typename T> + static void cleanupWithDelete(void *ptr) { + delete static_cast<T *>(ptr); + } + + template <typename T, void (T::*Fn)()> + static void cleanupWithMemberFunc(void *ptr) { + (static_cast<T *>(ptr)->*Fn)(); + } +}; + +/// \brief RAII object for triggering a cleanup of a DelayedCleanupPool. +class DelayedCleanupPoint { + DelayedCleanupPool &Pool; + +public: + DelayedCleanupPoint(DelayedCleanupPool &pool) : Pool(pool) { } + + ~DelayedCleanupPoint() { + Pool.doCleanup(); + } +}; + +} // end namespace clang + +#endif diff --git a/include/clang/Basic/Diagnostic.h b/include/clang/Basic/Diagnostic.h index fa763246d1b8..6f72976bfcf0 100644 --- a/include/clang/Basic/Diagnostic.h +++ b/include/clang/Basic/Diagnostic.h @@ -251,6 +251,14 @@ private: bool ErrorOccurred; bool FatalErrorOccurred; + /// \brief Indicates that an unrecoverable error has occurred. + bool UnrecoverableErrorOccurred; + + /// \brief Toggles for DiagnosticErrorTrap to check whether an error occurred + /// during a parsing section, e.g. during parsing a function. + bool TrapErrorOccurred; + bool TrapUnrecoverableErrorOccurred; + /// LastDiagLevel - This is the level of the last diagnostic emitted. This is /// used to emit continuation diagnostics with the same level as the /// diagnostic that they follow. @@ -269,13 +277,15 @@ private: /// can use this information to avoid redundancy across arguments. /// /// This is a hack to avoid a layering violation between libbasic and libsema. - typedef void (*ArgToStringFnTy)(ArgumentKind Kind, intptr_t Val, - const char *Modifier, unsigned ModifierLen, - const char *Argument, unsigned ArgumentLen, - const ArgumentValue *PrevArgs, - unsigned NumPrevArgs, - llvm::SmallVectorImpl<char> &Output, - void *Cookie); + typedef void (*ArgToStringFnTy)( + ArgumentKind Kind, intptr_t Val, + const char *Modifier, unsigned ModifierLen, + const char *Argument, unsigned ArgumentLen, + const ArgumentValue *PrevArgs, + unsigned NumPrevArgs, + llvm::SmallVectorImpl<char> &Output, + void *Cookie, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals); void *ArgToStringCookie; ArgToStringFnTy ArgToStringFn; @@ -432,7 +442,12 @@ public: bool hasErrorOccurred() const { return ErrorOccurred; } bool hasFatalErrorOccurred() const { return FatalErrorOccurred; } - + + /// \brief Determine whether any kind of unrecoverable error has occurred. + bool hasUnrecoverableErrorOccurred() const { + return FatalErrorOccurred || UnrecoverableErrorOccurred; + } + unsigned getNumWarnings() const { return NumWarnings; } void setNumWarnings(unsigned NumWarnings) { @@ -452,9 +467,11 @@ public: const char *Modifier, unsigned ModLen, const char *Argument, unsigned ArgLen, const ArgumentValue *PrevArgs, unsigned NumPrevArgs, - llvm::SmallVectorImpl<char> &Output) const { + llvm::SmallVectorImpl<char> &Output, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) const { ArgToStringFn(Kind, Val, Modifier, ModLen, Argument, ArgLen, - PrevArgs, NumPrevArgs, Output, ArgToStringCookie); + PrevArgs, NumPrevArgs, Output, ArgToStringCookie, + QualTypeVals); } void SetArgToStringFn(ArgToStringFnTy Fn, void *Cookie) { @@ -621,20 +638,28 @@ private: /// queried. class DiagnosticErrorTrap { Diagnostic &Diag; - unsigned PrevErrors; public: explicit DiagnosticErrorTrap(Diagnostic &Diag) - : Diag(Diag), PrevErrors(Diag.NumErrors) {} + : Diag(Diag) { reset(); } /// \brief Determine whether any errors have occurred since this /// object instance was created. bool hasErrorOccurred() const { - return Diag.NumErrors > PrevErrors; + return Diag.TrapErrorOccurred; + } + + /// \brief Determine whether any unrecoverable errors have occurred since this + /// object instance was created. + bool hasUnrecoverableErrorOccurred() const { + return Diag.TrapUnrecoverableErrorOccurred; } // Set to initial state of "no errors occurred". - void reset() { PrevErrors = Diag.NumErrors; } + void reset() { + Diag.TrapErrorOccurred = false; + Diag.TrapUnrecoverableErrorOccurred = false; + } }; //===----------------------------------------------------------------------===// diff --git a/include/clang/Basic/DiagnosticCategories.h b/include/clang/Basic/DiagnosticCategories.h new file mode 100644 index 000000000000..4dd067ba1e98 --- /dev/null +++ b/include/clang/Basic/DiagnosticCategories.h @@ -0,0 +1,26 @@ +//===- DiagnosticCategories.h - Diagnostic Categories Enumerators-*- C++ -*===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_BASIC_DIAGNOSTICCATEGORIES_H +#define LLVM_CLANG_BASIC_DIAGNOSTICCATEGORIES_H + +namespace clang { + namespace diag { + enum { +#define GET_CATEGORY_TABLE +#define CATEGORY(X, ENUM) ENUM, +#include "clang/Basic/DiagnosticGroups.inc" +#undef CATEGORY +#undef GET_CATEGORY_TABLE + DiagCat_NUM_CATEGORIES + }; + } // end namespace diag +} // end namespace clang + +#endif diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td index 50110fb53729..4b5de366cbc0 100644 --- a/include/clang/Basic/DiagnosticCommonKinds.td +++ b/include/clang/Basic/DiagnosticCommonKinds.td @@ -32,6 +32,7 @@ def note_type_being_defined : Note< def note_matching : Note<"to match this '%0'">; def note_using : Note<"using">; +def note_possibility : Note<"one possibility">; def note_also_found : Note<"also found">; // Parse && Lex diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 908a69b162c4..e33b67ef7a07 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -82,6 +82,12 @@ def err_drv_conflicting_deployment_targets : Error< "conflicting deployment targets, both '%0' and '%1' are present in environment">; def err_drv_invalid_arch_for_deployment_target : Error< "invalid architecture '%0' for deployment target '%1'">; +def err_drv_objc_gc_arr : Error< + "cannot specify both '-fobjc-arc' and '%0'">; +def err_arc_nonfragile_abi : Error< + "-fobjc-arc is not supported with fragile abi">; +def err_drv_mg_requires_m_or_mm : Error< + "option '-MG' requires '-M' or '-MM'">; def warn_c_kext : Warning< "ignoring -fapple-kext which is valid for c++ and objective-c++ only">; diff --git a/include/clang/Basic/DiagnosticFrontendKinds.td b/include/clang/Basic/DiagnosticFrontendKinds.td index 4aa85134aae4..120ba67dc1f6 100644 --- a/include/clang/Basic/DiagnosticFrontendKinds.td +++ b/include/clang/Basic/DiagnosticFrontendKinds.td @@ -137,6 +137,9 @@ def warn_pch_nonfragile_abi2 : Error< "PCH file was compiled with the %select{32-bit|enhanced non-fragile}0 " "Objective-C ABI but the %select{32-bit|enhanced non-fragile}1 " "Objective-C ABI is selected">; +def warn_pch_auto_ref_count : Error< + "PCH file was compiled %select{without|with} automated reference counting," + "which is currently %select{disabled|enabled}">; def warn_pch_apple_kext : Error< "PCH file was compiled %select{with|without}0 support for Apple's kernel " "extensions ABI but it is currently %select{disabled|enabled}1">; diff --git a/include/clang/Basic/DiagnosticGroups.td b/include/clang/Basic/DiagnosticGroups.td index 9abd6d3c5769..8a109149884f 100644 --- a/include/clang/Basic/DiagnosticGroups.td +++ b/include/clang/Basic/DiagnosticGroups.td @@ -62,6 +62,7 @@ def ExitTimeDestructors : DiagGroup<"exit-time-destructors">; def FourByteMultiChar : DiagGroup<"four-char-constants">; def GlobalConstructors : DiagGroup<"global-constructors">; def : DiagGroup<"idiomatic-parentheses">; +def BitwiseOpParentheses: DiagGroup<"bitwise-op-parentheses">; def LogicalOpParentheses: DiagGroup<"logical-op-parentheses">; def IgnoredQualifiers : DiagGroup<"ignored-qualifiers">; def : DiagGroup<"import">; @@ -115,6 +116,7 @@ def SignCompare : DiagGroup<"sign-compare">; def : DiagGroup<"stack-protector">; def : DiagGroup<"switch-default">; def : DiagGroup<"synth">; +def SizeofArrayArgument : DiagGroup<"sizeof-array-argument">; def TautologicalCompare : DiagGroup<"tautological-compare">; def HeaderHygiene : DiagGroup<"header-hygiene">; @@ -168,6 +170,15 @@ def ImplicitAtomic : DiagGroup<"implicit-atomic-properties">; def CustomAtomic : DiagGroup<"custom-atomic-properties">; def AtomicProperties : DiagGroup<"atomic-properties", [ImplicitAtomic, CustomAtomic]>; +def AutomaticReferenceCountingABI : DiagGroup<"arc-abi">; +def ARCUnsafeRetainedAssign : DiagGroup<"arc-unsafe-retained-assign">; +def ARCRetainCycles : DiagGroup<"arc-retain-cycles">; +def ARCNonPodMemAccess : DiagGroup<"arc-non-pod-memaccess">; +def AutomaticReferenceCounting : DiagGroup<"arc", + [AutomaticReferenceCountingABI, + ARCUnsafeRetainedAssign, + ARCRetainCycles, + ARCNonPodMemAccess]>; def Selector : DiagGroup<"selector">; def NonfragileAbi2 : DiagGroup<"nonfragile-abi2">; def Protocol : DiagGroup<"protocol">; @@ -192,7 +203,8 @@ def DuplicateArgDecl : DiagGroup<"duplicate-method-arg">; // in -Wparentheses because most users who use -Wparentheses explicitly // do not want these warnings. def Parentheses : DiagGroup<"parentheses", - [LogicalOpParentheses]>; + [LogicalOpParentheses, + BitwiseOpParentheses]>; // -Wconversion has its own warnings, but we split a few out for // legacy reasons: @@ -217,12 +229,12 @@ def Unused : DiagGroup<"unused", // Format settings. def FormatSecurity : DiagGroup<"format-security">; +def FormatY2K : DiagGroup<"format-y2k">; def Format : DiagGroup<"format", [FormatExtraArgs, FormatZeroLength, NonNull, - FormatSecurity]>, + FormatSecurity, FormatY2K]>, DiagCategory<"Format String Issue">; def FormatNonLiteral : DiagGroup<"format-nonliteral", [FormatSecurity]>; -def FormatY2K : DiagGroup<"format-y2k", [Format]>; def Format2 : DiagGroup<"format=2", [FormatNonLiteral, FormatSecurity, FormatY2K]>; @@ -248,6 +260,7 @@ def Most : DiagGroup<"most", [ ReturnType, SelfAssignment, Switch, + SizeofArrayArgument, Trigraphs, Uninitialized, UnknownPragmas, @@ -283,3 +296,6 @@ def GNU : DiagGroup<"gnu", [GNUDesignator, VLA]>; // A warning group for warnings about Microsoft extensions. def Microsoft : DiagGroup<"microsoft">; + +def ObjCNonUnifiedException : DiagGroup<"objc-nonunified-exceptions">; + diff --git a/include/clang/Basic/DiagnosticIDs.h b/include/clang/Basic/DiagnosticIDs.h index fa816de4fddb..ae4ed5bbb13c 100644 --- a/include/clang/Basic/DiagnosticIDs.h +++ b/include/clang/Basic/DiagnosticIDs.h @@ -227,6 +227,10 @@ private: /// suppressed. bool ProcessDiag(Diagnostic &Diag) const; + /// \brief Whether the diagnostic may leave the AST in a state where some + /// invariants can break. + bool isUnrecoverable(unsigned DiagID) const; + friend class Diagnostic; }; diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index 3514ccace22a..38d6a8001655 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -178,7 +178,7 @@ def ext_empty_fnmacro_arg : Extension< def err_pp_invalid_directive : Error<"invalid preprocessing directive">; def err_pp_hash_error : Error<"#error%0">; -def err_pp_file_not_found : Error<"'%0' file not found">, DefaultFatal; +def warn_pp_file_not_found : Warning<"'%0' file not found">, DefaultFatal; def err_pp_error_opening_file : Error< "error opening file '%0': %1">, DefaultFatal; def err_pp_empty_filename : Error<"empty filename">; diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index fb1c90950822..3764a4091546 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -263,6 +263,11 @@ def warn_objc_protocol_qualifier_missing_id : Warning< def err_objc_unknown_at : Error<"expected an Objective-C directive after '@'">; def err_illegal_super_cast : Error< "cannot cast 'super' (it isn't an expression)">; + +let CategoryName = "Automatic Reference Counting Issue" in { +def err_arc_bridge_retain : Error< + "unknown cast annotation __bridge_retain; did you mean __bridge_retained?">; +} def err_objc_illegal_visibility_spec : Error< "illegal visibility specification">; diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 5cfa61b3972d..97414f23d796 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -48,6 +48,8 @@ def err_vla_decl_has_static_storage : Error< "variable length array declaration can not have 'static' storage duration">; def err_vla_decl_has_extern_linkage : Error< "variable length array declaration can not have 'extern' linkage">; +def ext_vla_folded_to_constant : Extension< + "variable length array folded to constant array as an extension">; // C99 variably modified types def err_variably_modified_template_arg : Error< @@ -267,6 +269,15 @@ def warn_dyn_class_memaccess : Warning< InGroup<DiagGroup<"dynamic-class-memaccess">>; def note_bad_memaccess_silence : Note< "explicitly cast the pointer to silence this warning">; +def warn_sizeof_pointer_expr_memaccess : Warning< + "argument to 'sizeof' in %0 call is the same expression as the " + "%select{destination|source}1; did you mean to " + "%select{dereference it|remove the addressof|provide an explicit length}2?">, + InGroup<DiagGroup<"sizeof-pointer-memaccess">>; +def warn_sizeof_pointer_type_memaccess : Warning< + "argument to 'sizeof' in %0 call is the same pointer type %1 as the " + "%select{destination|source}2; expected %3 or an explicit length">, + InGroup<DiagGroup<"sizeof-pointer-memaccess">>; /// main() // static/inline main() are not errors in C, just in C++. @@ -319,6 +330,8 @@ def err_duplicate_class_def : Error< "duplicate interface definition for class %0">; def err_undef_superclass : Error< "cannot find interface declaration for %0, superclass of %1">; +def err_forward_superclass : Error< + "attempting to use the forward class %0 as superclass of %1">; def err_no_nsconstant_string_class : Error< "cannot find interface declaration for %0">; def err_recursive_superclass : Error< @@ -344,6 +357,8 @@ def err_class_extension_after_impl : Error< "cannot declare class extension for %0 after class implementation">; def note_implementation_declared : Note< "class implementation is declared here">; +def note_class_declared : Note< + "class is declared here">; def warn_dup_category_def : Warning< "duplicate definition of category %1 on interface %0">; def err_conflicting_super_class : Error<"conflicting super class name %0">; @@ -425,6 +440,15 @@ def warn_objc_property_copy_missing_on_block : Warning< def warn_atomic_property_rule : Warning< "writable atomic property %0 cannot pair a synthesized setter/getter " "with a user defined setter/getter">; +def warn_ownin_getter_rule : Warning< + "property's synthesized getter follows Cocoa naming" + " convention for returning 'owned' objects">; +def warn_property_getter_owning_mismatch : Warning< + "property declared as returning non-retained objects" + "; getter returning retained objects">; +def err_ownin_getter_rule : Error< + "property's synthesized getter follows Cocoa naming" + " convention for returning 'owned' objects">; def warn_default_atomic_custom_getter_setter : Warning< "atomic by default property %0 has a user defined %select{getter|setter}1 " "(property should be marked 'atomic' if this is intended)">, @@ -460,6 +484,12 @@ def error_bad_property_context : Error< def error_missing_property_ivar_decl : Error< "synthesized property %0 must either be named the same as a compatible" " ivar or must explicitly name an ivar">; +def error_synthesize_weak_non_arc_or_gc : Error< + "@synthesize of 'weak' property is only allowed in ARC or GC mode">; +def err_arc_perform_selector_retains : Error< + "performSelector names a selector which retains the object">; +def warn_arc_perform_selector_leaks : Warning< + "performSelector may cause a leak because its selector is unknown">; def error_synthesized_ivar_yet_not_supported : Error< "instance variable synthesis not yet supported" @@ -472,7 +502,7 @@ def error_ivar_in_superclass_use : Error< def error_weak_property : Error< "existing ivar %1 for __weak property %0 must be __weak">; def error_strong_property : Error< - "property %0 must be declared __weak to match existing ivar %1 with __weak attribute">; + "existing ivar %1 for strong property %0 may not be __weak">; def error_dynamic_property_ivar_decl : Error< "dynamic property can not have ivar specification">; def error_duplicate_ivar_use : Error< @@ -729,8 +759,6 @@ def err_not_integral_type_bitfield : Error< "bit-field %0 has non-integral type %1">; def err_not_integral_type_anon_bitfield : Error< "anonymous bit-field has non-integral type %0">; -def err_member_initialization : Error< - "fields can only be initialized in constructors">; def err_member_function_initialization : Error< "initializer on function does not look like a pure-specifier">; def err_non_virtual_pure : Error< @@ -1135,6 +1163,8 @@ def err_format_attribute_implicit_this_format_string : Error< "format attribute cannot specify the implicit this argument as the format " "string">; def warn_unknown_method_family : Warning<"unrecognized method family">; +def err_init_method_bad_return_type : Error< + "init methods must return an object pointer type, not %0">; def err_attribute_invalid_size : Error< "vector size not an integral multiple of component size">; def err_attribute_zero_size : Error<"zero vector size">; @@ -1160,6 +1190,10 @@ def err_as_qualified_auto_decl : Error< "automatic variable qualified with an address space">; def err_arg_with_address_space : Error< "parameter may not be qualified with an address space">; +def err_attr_objc_ownership_bad_type : Error< + "the type %0 cannot be retained">; +def err_attr_objc_ownership_redundant : Error< + "the type %0 already has retainment attributes set on it">; def err_attribute_not_string : Error< "argument to %0 attribute was not a string literal">; def err_attribute_section_invalid_for_target : Error< @@ -1215,6 +1249,8 @@ def warn_function_attribute_wrong_type : Warning< "'%0' only applies to function types; type here is %1">; def warn_pointer_attribute_wrong_type : Warning< "'%0' only applies to pointer types; type here is %1">; +def warn_objc_object_attribute_wrong_type : Warning< + "'%0' only applies to objective-c object or block pointer types; type here is %1">; def warn_gnu_inline_attribute_requires_inline : Warning< "'gnu_inline' attribute requires function to be marked 'inline'," " attribute ignored">; @@ -1232,6 +1268,13 @@ def err_cconv_varargs : Error< def err_regparm_mismatch : Error<"function declared with with regparm(%0) " "attribute was previously declared " "%plural{0:without the regparm|:with the regparm(%1)}1 attribute">; +def err_objc_precise_lifetime_bad_type : Error< + "objc_precise_lifetime only applies to retainable types; type here is %0">; +def warn_objc_precise_lifetime_meaningless : Error< + "objc_precise_lifetime is not meaningful for " + "%select{__unsafe_unretained|__autoreleasing}0 objects">; +def warn_label_attribute_not_unused : Warning< + "The only valid attribute for labels is 'unused'">; def err_invalid_pcs : Error<"Invalid PCS type">; // Availability attribute @@ -1505,7 +1548,13 @@ def note_ovl_candidate_arity : Note<"candidate " def note_ovl_candidate_deleted : Note< "candidate %select{function|function|constructor|" - "function |function |constructor ||||constructor (inherited)}0%1 " + "function |function |constructor |" + "constructor (the implicit default constructor)|" + "constructor (the implicit copy constructor)|" + "constructor (the implicit move constructor)|" + "function (the implicit copy assignment operator)|" + "function (the implicit move assignment operator)|" + "constructor (inherited)}0%1 " "has been explicitly %select{made unavailable|deleted}2">; // Giving the index of the bad argument really clutters this message, and @@ -1565,7 +1614,18 @@ def note_ovl_candidate_bad_gc : Note<"candidate " "function (the implicit move assignment operator)|" "constructor (inherited)}0%1 not viable: " "%select{%ordinal6|'this'}5 argument (%2) has %select{no|__weak|__strong}3 " - "lifetime, but parameter has %select{no|__weak|__strong}4 lifetime">; + "ownership, but parameter has %select{no|__weak|__strong}4 ownership">; +def note_ovl_candidate_bad_ownership : Note<"candidate " + "%select{function|function|constructor|" + "function |function |constructor |" + "constructor (the implicit default constructor)|" + "constructor (the implicit copy constructor)|" + "function (the implicit copy assignment operator)|" + "constructor (inherited)}0%1 not viable: " + "%select{%ordinal6|'this'}5 argument (%2) has " + "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}3 ownership," + " but parameter has %select{no|__unsafe_unretained|__strong|__weak|" + "__autoreleasing}4 ownership">; def note_ovl_candidate_bad_cvr_this : Note<"candidate " "%select{|function|||function||||" "function (the implicit copy assignment operator)|}0 not viable: " @@ -1871,8 +1931,11 @@ def err_not_class_template_specialization : Error< "parameter}0">; def err_function_specialization_in_class : Error< "cannot specialize a function %0 within class scope">; -def err_explicit_specialization_storage_class : Error< +def ext_explicit_specialization_storage_class : ExtWarn< "explicit specialization cannot have a storage class">; +def err_explicit_specialization_inconsistent_storage_class : Error< + "explicit specialization has extraneous, inconsistent storage class " + "'%select{none|extern|static|__private_extern__|auto|register}0'">; // C++ class template specializations and out-of-line definitions def err_template_spec_needs_header : Error< @@ -2180,7 +2243,7 @@ def err_undeclared_var_use : Error<"use of undeclared identifier %0">; def note_dependent_var_use : Note<"must qualify identifier to find this " "declaration in dependent base class">; def err_not_found_by_two_phase_lookup : Error<"call to function %0 that is neither " - "visible in the template definition nor found by argument dependent lookup">; + "visible in the template definition nor found by argument-dependent lookup">; def note_not_found_by_two_phase_lookup : Note<"%0 should be declared prior to the " "call site%select{| or in %2| or in an associated namespace of one of its arguments}1">; def err_undeclared_use : Error<"use of undeclared %0">; @@ -2199,8 +2262,8 @@ def err_unavailable_message : Error<"%0 is unavailable: %1">; def warn_unavailable_fwdclass_message : Warning< "%0 maybe unavailable because receiver type is unknown">; def note_unavailable_here : Note< - "function has been explicitly marked " - "%select{unavailable|deleted|deprecated}0 here">; + "%select{declaration|function}0 has been explicitly marked " + "%select{unavailable|deleted|deprecated}1 here">; def warn_not_enough_argument : Warning< "not enough variable arguments in %0 declaration to fit a sentinel">; def warn_missing_sentinel : Warning < @@ -2237,6 +2300,8 @@ def err_inline_declaration_block_scope : Error< "inline declaration of %0 not allowed in block scope">; def err_static_non_static : Error< "static declaration of %0 follows non-static declaration">; +def warn_weak_import : Warning < + "an already-declared variable is made a weak_import declaration %0">; def warn_static_non_static : ExtWarn< "static declaration of %0 follows non-static declaration">; def err_non_static_static : Error< @@ -2418,6 +2483,8 @@ def err_indirect_goto_in_protected_scope : Error< def note_indirect_goto_target : Note<"possible target of indirect goto">; def note_protected_by_variable_init : Note< "jump bypasses variable initialization">; +def note_protected_by_variable_nontriv_destructor : Note< + "jump bypasses variable with a non-trivial destructor">; def note_protected_by_cleanup : Note< "jump bypasses initialization of variable with __attribute__((cleanup))">; def note_protected_by_vla_typedef : Note< @@ -2434,12 +2501,22 @@ def note_protected_by_objc_finally : Note< "jump bypasses initialization of @finally block">; def note_protected_by_objc_synchronized : Note< "jump bypasses initialization of @synchronized block">; +def note_protected_by_objc_autoreleasepool : Note< + "jump bypasses auto release push of @autoreleasepool block">; def note_protected_by_cxx_try : Note< "jump bypasses initialization of try block">; def note_protected_by_cxx_catch : Note< "jump bypasses initialization of catch block">; def note_protected_by___block : Note< "jump bypasses setup of __block variable">; +def note_protected_by_objc_ownership : Note< + "jump bypasses initialization of retaining variable">; +def note_enters_block_captures_cxx_obj : Note< + "jump enters lifetime of block which captures a destructible c++ object">; +def note_enters_block_captures_strong : Note< + "jump enters lifetime of block which strongly captures a variable">; +def note_enters_block_captures_weak : Note< + "jump enters lifetime of block which weakly captures a variable">; def note_exits_cleanup : Note< "jump exits scope of variable with __attribute__((cleanup))">; @@ -2459,6 +2536,16 @@ def note_exits_cxx_try : Note< "jump exits try block">; def note_exits_cxx_catch : Note< "jump exits catch block">; +def note_exits_objc_autoreleasepool : Note< + "jump exits autoreleasepool block">; +def note_exits_objc_ownership : Note< + "jump exits scope of retaining variable">; +def note_exits_block_captures_cxx_obj : Note< + "jump exits lifetime of block which captures a destructible c++ object">; +def note_exits_block_captures_strong : Note< + "jump exits lifetime of block which strongly captures a variable">; +def note_exits_block_captures_weak : Note< + "jump exits lifetime of block which weakly captures a variable">; def err_func_returning_array_function : Error< "function cannot return %select{array|function}0 type %1">; @@ -2490,6 +2577,152 @@ def ext_flexible_array_empty_aggregate_gnu : Extension< def ext_flexible_array_union_gnu : Extension< "flexible array member %0 in a union is a GNU extension">, InGroup<GNU>; +let CategoryName = "Automatic Reference Counting Issue" in { + +// ARC-mode diagnostics. +def err_arc_weak_no_runtime : Error< + "the current deployment target does not support automated __weak references">; +def err_arc_unsupported_weak_class : Error< + "class is incompatible with __weak references">; +def err_arc_weak_unavailable_assign : Error< + "assignment of a weak-unavailable object to a __weak object">; +def err_arc_convesion_of_weak_unavailable : Error< + "%select{implicit conversion|cast}0 of weak-unavailable object of type %1 to" + " a __weak object of type %2">; +def err_arc_illegal_explicit_message : Error< + "ARC forbids explicit message send of %0">; +def err_arc_unused_init_message : Error< + "the result of a delegate init call must be immediately returned " + "or assigned to 'self'">; +def err_arc_mismatched_cast : Error< + "%select{implicit conversion|cast}0 of " + "%select{%2|a non-Objective-C pointer type %2|a block pointer|" + "an Objective-C pointer|an indirect pointer to an Objective-C pointer}1" + " to %3 is disallowed with ARC">; +def err_arc_objc_object_in_struct : Error< + "ARC forbids Objective-C objects in structs or unions">; +def err_arc_objc_property_default_assign_on_object : Error< + "ARC forbids synthesizing a property of an Objective-C object " + "with unspecified storage attribute">; +def err_arc_illegal_selector : Error< + "ARC forbids use of %0 in a @selector">; +def err_arc_illegal_method_def : Error< + "ARC forbids implementation of %0">; +def err_arc_lost_method_convention : Error< + "method was declared as %select{an 'alloc'|a 'copy'|an 'init'|a 'new'}0 " + "method, but its implementation doesn't match because %select{" + "its result type is not an object pointer|" + "its result type is unrelated to its receiver type}1">; +def note_arc_lost_method_convention : Note<"declaration in interface">; +def err_arc_gained_method_convention : Error< + "method implementation does not match its declaration">; +def note_arc_gained_method_convention : Note< + "declaration in interface is not in the '%select{alloc|copy|init|new}0' " + "family because %select{its result type is not an object pointer|" + "its result type is unrelated to its receiver type}1">; +def err_typecheck_arr_assign_self : Error< + "cannot assign to 'self' outside of a method in the init family">; +def err_typecheck_arr_assign_enumeration : Error< + "fast enumeration variables can't be modified in ARC by default; " + "declare the variable __strong to allow this">; +def warn_arc_non_pod_class_with_object_member : Warning< + "%0 cannot be shared between ARC and non-ARC " + "code; add a copy constructor, a copy assignment operator, and a destructor " + "to make it ABI-compatible">, InGroup<AutomaticReferenceCountingABI>, + DefaultIgnore; +def warn_arc_retained_assign : Warning< + "assigning retained object to %select{weak|unsafe_unretained}0 variable" + "; object will be released after assignment">, + InGroup<ARCUnsafeRetainedAssign>; +def warn_arc_retained_property_assign : Warning< + "assigning retained object to unsafe property" + "; object will be released after assignment">, + InGroup<ARCUnsafeRetainedAssign>; +def warn_arc_trivial_member_function_with_object_member : Warning< + "%0 cannot be shared between ARC and non-ARC " + "code; add a non-trivial %select{copy constructor|copy assignment operator|" + "destructor}1 to make it ABI-compatible">, + InGroup<AutomaticReferenceCountingABI>, DefaultIgnore; +def err_arc_new_array_without_ownership : Error< + "'new' cannot allocate an array of %0 with no explicit ownership">; +def warn_err_new_delete_object_array : Warning< + "%select{allocating|destroying}0 an array of %1; this array must not " + "%select{be deleted in|have been allocated from}0 non-ARC code">, + InGroup<AutomaticReferenceCountingABI>, DefaultIgnore; +def err_arc_autoreleasing_var : Error< + "%select{__block variables|global variables|fields|ivars}0 cannot have " + "__autoreleasing ownership">; +def err_arc_thread_ownership : Error< + "thread-local variable has non-trivial ownership: type is %0">; +def err_arc_indirect_no_ownership : Error< + "%select{pointer|reference}1 to non-const type %0 with no explicit ownership">, + InGroup<AutomaticReferenceCounting>; +def err_arc_array_param_no_ownership : Error< + "must explicitly describe intended ownership of an object array parameter">; +def err_arc_pseudo_dtor_inconstant_quals : Error< + "pseudo-destructor destroys object of type %0 with inconsistently-qualified " + "type %1">; +def err_arc_init_method_unrelated_result_type : Error< + "init methods must return a type related to the receiver type">; +def err_arc_nonlocal_writeback : Error< + "passing address of %select{non-local|non-scalar}0 object to " + "__autoreleasing parameter for write-back">; +def err_arc_method_not_found : Error< + "no known %select{instance|class}1 method for selector %0">; +def err_arc_receiver_forward_class : Error< + "receiver %0 for class message is a forward declaration">; +def err_arc_may_not_respond : Error< + "receiver type %0 for instance message does not declare a method with " + "selector %1">; +def err_arc_receiver_forward_instance : Error< + "receiver type %0 for instance message is a forward declaration">; +def err_arc_multiple_method_decl : Error< + "multiple methods named %0 found with mismatched result, " + "parameter type or attributes">; +def warn_arc_retain_cycle : Warning< + "capturing %0 strongly in this block is likely to lead to a retain cycle">, + InGroup<ARCRetainCycles>; +def note_arc_retain_cycle_owner : Note< + "block will be retained by %select{the captured object|an object strongly " + "retained by the captured object}0">; +def note_nontrivial_objc_ownership : Note< + "because type %0 has %select{no|no|__strong|__weak|__autoreleasing}1 " + "ownership">; +def warn_arc_object_memaccess : Warning< + "%select{destination for|source of}0 this %1 call is a pointer to " + "ownership-qualified type %2">, InGroup<ARCNonPodMemAccess>; + +def err_arc_strong_property_ownership : Error< + "existing ivar %1 for strong property %0 may not be " + "%select{|__unsafe_unretained||__weak}2">; +def err_arc_assign_property_ownership : Error< + "existing ivar %1 for unsafe_unretained property %0 must be __unsafe_unretained">; +def err_arc_inconsistent_property_ownership : Error< + "%select{strong|weak|unsafe_unretained}1 property %0 may not also be " + "declared %select{|__unsafe_unretained|__strong|__weak|__autoreleasing}2">; +def err_arc_atomic_ownership : Error< + "cannot perform atomic operation on a pointer to type %0: type has " + "non-trivial ownership">; + +def err_arc_bridge_cast_incompatible : Error< + "incompatible types casting %0 to %1 with a %select{__bridge|" + "__bridge_transfer|__bridge_retained}2 cast">; +def err_arc_bridge_cast_wrong_kind : Error< + "cast of %select{Objective-C|block|C}0 pointer type %1 to " + "%select{Objective-C|block|C}2 pointer type %3 cannot use %select{__bridge|" + "__bridge_transfer|__bridge_retained}4">; +def err_arc_cast_requires_bridge : Error< + "cast of %select{Objective-C|block|C}0 pointer type %1 to " + "%select{Objective-C|block|C}2 pointer type %3 requires a bridged cast">; +def note_arc_bridge : Note< + "use __bridge to convert directly (no change in ownership)">; +def note_arc_bridge_transfer : Note< + "use __bridge_transfer to transfer ownership of a +1 %0 into ARC">; +def note_arc_bridge_retained : Note< + "use __bridge_retained to make an ARC object available as a +1 %0">; + +} // ARC category name + def err_flexible_array_init_needs_braces : Error< "flexible array requires brace-enclosed initializer">; def err_illegal_decl_array_of_functions : Error< @@ -2563,11 +2796,11 @@ def warn_remainder_by_zero : Warning<"remainder by zero is undefined">; def warn_shift_negative : Warning<"shift count is negative">; def warn_shift_gt_typewidth : Warning<"shift count >= width of type">; def warn_shift_result_gt_typewidth : Warning< - "shift result (%0) requires %1 bits to represent, but %2 only has %3 bits">, - InGroup<DiagGroup<"shift-overflow">>; -def warn_shift_result_overrides_sign_bit : Warning< - "shift result (%0) overrides the sign bit of the shift expression's type " - "(%1) and becomes negative">, + "signed shift result (%0) requires %1 bits to represent, but %2 only has " + "%3 bits">, InGroup<DiagGroup<"shift-overflow">>; +def warn_shift_result_sets_sign_bit : Warning< + "signed shift result (%0) sets the sign bit of the shift expression's " + "type (%1) and becomes negative">, InGroup<DiagGroup<"shift-sign-overflow">>, DefaultIgnore; def warn_precedence_bitwise_rel : Warning< @@ -2579,17 +2812,22 @@ def note_precedence_bitwise_silence : Note< "place parentheses around the %0 expression to silence this warning">; def warn_precedence_conditional : Warning< - "?: has lower precedence than %0; %0 will be evaluated first">, + "operator '?:' has lower precedence than '%0'; '%0' will be evaluated first">, InGroup<Parentheses>; def note_precedence_conditional_first : Note< - "place parentheses around the ?: expression to evaluate it first">; + "place parentheses around the '?:' expression to evaluate it first">; def note_precedence_conditional_silence : Note< - "place parentheses around the %0 expression to silence this warning">; + "place parentheses around the '%0' expression to silence this warning">; def warn_logical_instead_of_bitwise : Warning< "use of logical %0 with constant operand; switch to bitwise %1 or " "remove constant">, InGroup<DiagGroup<"constant-logical-operand">>; +def warn_bitwise_and_in_bitwise_or : Warning< + "'&' within '|'">, InGroup<BitwiseOpParentheses>; +def note_bitwise_and_in_bitwise_or_silence : Note< + "place parentheses around the '&' expression to silence this warning">; + def warn_logical_and_in_logical_or : Warning< "'&&' within '||'">, InGroup<LogicalOpParentheses>; def note_logical_and_in_logical_or_silence : Note< @@ -2599,6 +2837,10 @@ def warn_self_assignment : Warning< "explicitly assigning a variable of type %0 to itself">, InGroup<SelfAssignment>, DefaultIgnore; +def warn_sizeof_array_param : Warning< + "sizeof on array function parameter will return size of %0 instead of %1">, + InGroup<SizeofArrayArgument>; + def err_sizeof_nonfragile_interface : Error< "invalid application of '%select{alignof|sizeof}1' to interface %0 in " "non-fragile ABI">; @@ -2623,10 +2865,15 @@ def err_subscript_function_type : Error< "subscript of pointer to function type %0">; def err_subscript_incomplete_type : Error< "subscript of pointer to incomplete type %0">; +def ext_gnu_subscript_void_type : Extension< + "subscript of a pointer to void is a GNU extension">, InGroup<PointerArith>; def err_typecheck_member_reference_struct_union : Error< "member reference base type %0 is not a structure or union">; def err_typecheck_member_reference_ivar : Error< "%0 does not have a member named %1">; +def error_arc_weak_ivar_access : Error< + "dereferencing a __weak pointer is not allowed due to possible " + "null value caused by race condition, assign it to strong variable first">; def err_typecheck_member_reference_arrow : Error< "member reference type %0 is not a pointer">; def err_typecheck_member_reference_suggestion : Error< @@ -2658,7 +2905,7 @@ def err_member_def_undefined_record : Error< def err_member_def_does_not_match : Error< "out-of-line definition of %0 does not match any declaration in %1">; def err_member_def_does_not_match_ret_type : Error< - "out-of-line definition of %q0 differ from the declaration in the return type">; + "out-of-line definition of %q0 differs from the declaration in the return type">; def err_nonstatic_member_out_of_line : Error< "non-static data member defined out-of-line">; def err_nonstatic_flexible_variable : Error< @@ -2682,11 +2929,12 @@ def err_ivar_reference_type : Error< def err_typecheck_illegal_increment_decrement : Error< "cannot %select{decrement|increment}1 value of type %0">; def err_typecheck_arithmetic_incomplete_type : Error< - "arithmetic on pointer to incomplete type %0">; + "arithmetic on a pointer to an incomplete type %0">; def err_typecheck_pointer_arith_function_type : Error< - "arithmetic on pointer to function type %0">; + "arithmetic on%select{ a|}0 pointer%select{|s}0 to%select{ the|}2 " + "function type%select{|s}2 %1%select{| and %3}2">; def err_typecheck_pointer_arith_void_type : Error< - "arithmetic on pointer to void type">; + "arithmetic on%select{ a|}0 pointer%select{|s}0 to void">; def err_typecheck_decl_incomplete_type : Error< "variable has incomplete type %0">; def ext_typecheck_decl_incomplete_type : ExtWarn< @@ -2803,6 +3051,9 @@ def warn_runsigned_always_true_comparison : Warning< def warn_comparison_of_mixed_enum_types : Warning< "comparison of two values with different enumeration types (%0 and %1)">, InGroup<DiagGroup<"enum-compare">>; +def warn_null_in_arithmetic_operation : Warning< + "use of NULL in arithmetic operation">, + InGroup<DiagGroup<"null-arithmetic">>; def err_invalid_this_use : Error< "invalid use of 'this' outside of a nonstatic member function">; @@ -2871,9 +3122,11 @@ def note_forward_class : Note< def err_duplicate_property : Error< "property has a previous declaration">; def ext_gnu_void_ptr : Extension< - "use of GNU void* extension">, InGroup<PointerArith>; + "arithmetic on%select{ a|}0 pointer%select{|s}0 to void is a GNU extension">, + InGroup<PointerArith>; def ext_gnu_ptr_func_arith : Extension< - "arithmetic on pointer to function type %0 is a GNU extension">, + "arithmetic on%select{ a|}0 pointer%select{|s}0 to%select{ the|}2 function " + "type%select{|s}2 %1%select{| and %3}2 is a GNU extension">, InGroup<PointerArith>; def error_readonly_property_assignment : Error< "assigning to property with 'readonly' attribute not allowed">; @@ -2942,9 +3195,9 @@ def err_qualified_objc_catch_parm : Error< "@catch parameter declarator cannot be qualified">; def err_objc_pointer_cxx_catch_gnu : Error< "can't catch Objective C exceptions in C++ in the GNU runtime">; -def err_objc_pointer_cxx_catch_fragile : Error< - "can't catch Objective C exceptions in C++ in the non-unified " - "exception model">; +def warn_objc_pointer_cxx_catch_fragile : Warning< + "can not catch an exception thrown with @throw in C++ in the non-unified " + "exception model">, InGroup<ObjCNonUnifiedException>; def err_objc_object_catch : Error< "can't catch an Objective C object by value">; def err_incomplete_type_objc_at_encode : Error< @@ -3328,6 +3581,15 @@ def err_typecheck_incompatible_address_space : Error< "|sending %0 to parameter of type %1" "|casting %0 to type %1}2" " changes address space of pointer">; +def err_typecheck_incompatible_ownership : Error< + "%select{assigning %1 to %0" + "|passing %0 to parameter of type %1" + "|returning %0 from a function with result type %1" + "|converting %0 to type %1" + "|initializing %0 with an expression of type %1" + "|sending %0 to parameter of type %1" + "|casting %0 to type %1}2" + " changes retain/release properties of pointer">; def err_typecheck_convert_ambiguous : Error< "ambiguity in initializing value of type %0 with initializer of type %1">; def err_typecheck_comparison_of_distinct_blocks : Error< @@ -3544,6 +3806,9 @@ def ext_in_class_initializer_float_type : ExtWarn< def err_in_class_initializer_non_constant : Error< "in-class initializer is not a constant expression">; +def ext_in_class_initializer_non_constant : Extension< + "in-class initializer is not a constant expression, accepted as an extension">; + // C++ anonymous unions and GNU anonymous structs/unions def ext_anonymous_union : Extension< "anonymous unions are a GNU extension in C">, InGroup<GNU>; @@ -3929,12 +4194,23 @@ def err_switch_incomplete_class_type : Error< "switch condition has incomplete class type %0">; def warn_empty_if_body : Warning< "if statement has empty body">, InGroup<EmptyBody>; + def err_va_start_used_in_non_variadic_function : Error< "'va_start' used in function with fixed args">; def warn_second_parameter_of_va_start_not_last_named_argument : Warning< "second parameter of 'va_start' not last named argument">; def err_first_argument_to_va_arg_not_of_type_va_list : Error< "first argument to 'va_arg' is of type %0 and not 'va_list'">; +def err_second_parameter_to_va_arg_incomplete: Error< + "second argument to 'va_arg' is of incomplete type %0">; +def err_second_parameter_to_va_arg_abstract: Error< + "second argument to 'va_arg' is of abstract type %0">; +def warn_second_parameter_to_va_arg_not_pod : Warning< + "second argument to 'va_arg' is of non-POD type %0">, + InGroup<DiagGroup<"non-pod-varargs">>, DefaultError; +def warn_second_parameter_to_va_arg_never_compatible : Warning< + "second argument to 'va_arg' is of promotable type %0; this va_arg has " + "undefined behavior because arguments will be promoted to %1">; def warn_return_missing_expr : Warning< "non-void %select{function|method}1 %0 should return a value">, DefaultError, @@ -3943,8 +4219,9 @@ def ext_return_missing_expr : ExtWarn< "non-void %select{function|method}1 %0 should return a value">, DefaultError, InGroup<ReturnType>; def ext_return_has_expr : ExtWarn< - "void %select{function|method}1 %0 should not return a value">, DefaultError, - InGroup<ReturnType>; + "%select{void function|void method|constructor|destructor}1 %0 " + "should not return a value">, + DefaultError, InGroup<ReturnType>; def ext_return_has_void_expr : Extension< "void %select{function|method}1 %0 should not return void expression">; def warn_noreturn_function_has_return_expr : Warning< @@ -4074,6 +4351,11 @@ def err_typecheck_member_reference_ivar_suggest : Error< "%0 does not have a member named %1; did you mean %2?">; def err_property_not_found_suggest : Error< "property %0 not found on object of type %1; did you mean %2?">; +def err_ivar_access_using_property_syntax_suggest : Error< + "property %0 not found on object of type %1; did you mean to access ivar %2?">; +def err_property_found_suggest : Error< + "property %0 found on object of type %1; did you mean to access " + "it with the \".\" operator?">; def err_undef_interface_suggest : Error< "cannot find interface declaration for %0; did you mean %1?">; def warn_undef_interface_suggest : Warning< diff --git a/include/clang/Basic/FileManager.h b/include/clang/Basic/FileManager.h index 2ca344d55370..1324533fa03e 100644 --- a/include/clang/Basic/FileManager.h +++ b/include/clang/Basic/FileManager.h @@ -21,10 +21,13 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/Support/Allocator.h" -#include "llvm/Config/config.h" // for mode_t // FIXME: Enhance libsystem to support inode and other fields in stat. #include <sys/types.h> +#ifdef _MSC_VER +typedef unsigned short mode_t; +#endif + struct stat; namespace llvm { diff --git a/include/clang/Basic/IdentifierTable.h b/include/clang/Basic/IdentifierTable.h index b4eca6d8fbdb..bebcffdddede 100644 --- a/include/clang/Basic/IdentifierTable.h +++ b/include/clang/Basic/IdentifierTable.h @@ -411,23 +411,15 @@ public: return II; } - IdentifierInfo &get(const char *NameStart, const char *NameEnd) { - return get(llvm::StringRef(NameStart, NameEnd-NameStart)); - } - - IdentifierInfo &get(const char *Name, size_t NameLen) { - return get(llvm::StringRef(Name, NameLen)); - } - /// \brief Gets an IdentifierInfo for the given name without consulting /// external sources. /// /// This is a version of get() meant for external sources that want to /// introduce or modify an identifier. If they called get(), they would /// likely end up in a recursion. - IdentifierInfo &getOwn(const char *NameStart, const char *NameEnd) { + IdentifierInfo &getOwn(llvm::StringRef Name) { llvm::StringMapEntry<IdentifierInfo*> &Entry = - HashTable.GetOrCreateValue(NameStart, NameEnd); + HashTable.GetOrCreateValue(Name); IdentifierInfo *II = Entry.getValue(); if (!II) { @@ -444,9 +436,6 @@ public: return *II; } - IdentifierInfo &getOwn(llvm::StringRef Name) { - return getOwn(Name.begin(), Name.end()); - } typedef HashTableTy::const_iterator iterator; typedef HashTableTy::const_iterator const_iterator; @@ -499,7 +488,10 @@ enum ObjCMethodFamily { OMF_release, OMF_retain, OMF_retainCount, - OMF_self + OMF_self, + + // performSelector families + OMF_performSelector }; /// Enough bits to store any enumerator in ObjCMethodFamily or diff --git a/include/clang/Basic/LangOptions.h b/include/clang/Basic/LangOptions.h index f0f1432ca0b8..dc77d4c1496b 100644 --- a/include/clang/Basic/LangOptions.h +++ b/include/clang/Basic/LangOptions.h @@ -119,6 +119,8 @@ public: unsigned InlineVisibilityHidden : 1; // Whether inline C++ methods have // hidden visibility by default. unsigned ParseUnknownAnytype: 1; /// Let the user write __unknown_anytype. + unsigned DebuggerSupport : 1; /// Do things that only make sense when + /// supporting a debugger unsigned SpellChecking : 1; // Whether to perform spell-checking for error // recovery. @@ -130,6 +132,10 @@ public: unsigned DefaultFPContract : 1; // Default setting for FP_CONTRACT // FIXME: This is just a temporary option, for testing purposes. unsigned NoBitFieldTypeAlign : 1; + unsigned ObjCAutoRefCount : 1; // Objective C automated reference counting + unsigned ObjCRuntimeHasWeak : 1; // The ARC runtime supports __weak + unsigned ObjCInferRelatedReturnType : 1; // Infer Objective-C related return + // types unsigned FakeAddressSpaceMap : 1; // Use a fake address space map, for // testing languages such as OpenCL. @@ -172,10 +178,13 @@ public: Trigraphs = BCPLComment = Bool = DollarIdents = AsmPreprocessor = 0; GNUMode = GNUKeywords = ImplicitInt = Digraphs = 0; HexFloats = 0; + ObjCAutoRefCount = 0; + ObjCRuntimeHasWeak = 0; + ObjCInferRelatedReturnType = 0; GC = ObjC1 = ObjC2 = ObjCNonFragileABI = ObjCNonFragileABI2 = 0; AppleKext = 0; ObjCDefaultSynthProperties = 0; - ObjCInferRelatedResultType = 0; + ObjCInferRelatedResultType = 1; NoConstantCFStrings = 0; InlineVisibilityHidden = 0; C99 = C1X = Microsoft = Borland = CPlusPlus = CPlusPlus0x = 0; CXXOperatorNames = PascalStrings = WritableStrings = ConstStrings = 0; @@ -234,7 +243,7 @@ public: FakeAddressSpaceMap = 0; MRTD = 0; DelayedTemplateParsing = 0; - ParseUnknownAnytype = 0; + ParseUnknownAnytype = DebuggerSupport = 0; } GCMode getGCMode() const { return (GCMode) GC; } diff --git a/include/clang/Basic/SourceManager.h b/include/clang/Basic/SourceManager.h index df5074cb42e6..6301f3197859 100644 --- a/include/clang/Basic/SourceManager.h +++ b/include/clang/Basic/SourceManager.h @@ -36,6 +36,7 @@ class SourceManager; class FileManager; class FileEntry; class LineTableInfo; +class LangOptions; /// SrcMgr - Public enums and private classes that are part of the /// SourceManager implementation. @@ -89,7 +90,7 @@ namespace SrcMgr { /// getBuffer - Returns the memory buffer for the associated content. /// - /// \param Diag Object through which diagnostics will be emitted it the + /// \param Diag Object through which diagnostics will be emitted if the /// buffer cannot be retrieved. /// /// \param Loc If specified, is the location that invalid file diagnostics @@ -238,8 +239,11 @@ namespace SrcMgr { /// InstantiationLocStart/InstantiationLocEnd - In a macro expansion, these /// indicate the start and end of the instantiation. In object-like macros, /// these will be the same. In a function-like macro instantiation, the - /// start will be the identifier and the end will be the ')'. + /// start will be the identifier and the end will be the ')'. Finally, in + /// macro-argument instantitions, the end will be 'SourceLocation()', an + /// invalid location. unsigned InstantiationLocStart, InstantiationLocEnd; + public: SourceLocation getSpellingLoc() const { return SourceLocation::getFromRawEncoding(SpellingLoc); @@ -248,7 +252,9 @@ namespace SrcMgr { return SourceLocation::getFromRawEncoding(InstantiationLocStart); } SourceLocation getInstantiationLocEnd() const { - return SourceLocation::getFromRawEncoding(InstantiationLocEnd); + SourceLocation EndLoc = + SourceLocation::getFromRawEncoding(InstantiationLocEnd); + return EndLoc.isInvalid() ? getInstantiationLocStart() : EndLoc; } std::pair<SourceLocation,SourceLocation> getInstantiationLocRange() const { @@ -256,19 +262,52 @@ namespace SrcMgr { getInstantiationLocEnd()); } - /// get - Return a InstantiationInfo for an expansion. IL specifies - /// the instantiation location (where the macro is expanded), and SL - /// specifies the spelling location (where the characters from the token - /// come from). IL and PL can both refer to normal File SLocs or + bool isMacroArgInstantiation() const { + // Note that this needs to return false for default constructed objects. + return getInstantiationLocStart().isValid() && + SourceLocation::getFromRawEncoding(InstantiationLocEnd).isInvalid(); + } + + /// create - Return a InstantiationInfo for an expansion. ILStart and + /// ILEnd specify the instantiation range (where the macro is expanded), + /// and SL specifies the spelling location (where the characters from the + /// token come from). All three can refer to normal File SLocs or /// instantiation locations. - static InstantiationInfo get(SourceLocation ILStart, SourceLocation ILEnd, - SourceLocation SL) { + static InstantiationInfo create(SourceLocation SL, + SourceLocation ILStart, + SourceLocation ILEnd) { InstantiationInfo X; X.SpellingLoc = SL.getRawEncoding(); X.InstantiationLocStart = ILStart.getRawEncoding(); X.InstantiationLocEnd = ILEnd.getRawEncoding(); return X; } + + /// createForMacroArg - Return a special InstantiationInfo for the + /// expansion of a macro argument into a function-like macro's body. IL + /// specifies the instantiation location (where the macro is expanded). + /// This doesn't need to be a range because a macro is always instantiated + /// at a macro parameter reference, and macro parameters are always exactly + /// one token. SL specifies the spelling location (where the characters + /// from the token come from). IL and SL can both refer to normal File + /// SLocs or instantiation locations. + /// + /// Given the code: + /// \code + /// #define F(x) f(x) + /// F(42); + /// \endcode + /// + /// When expanding '\c F(42)', the '\c x' would call this with an SL + /// pointing at '\c 42' anad an IL pointing at its location in the + /// definition of '\c F'. + static InstantiationInfo createForMacroArg(SourceLocation SL, + SourceLocation IL) { + // We store an intentionally invalid source location for the end of the + // instantiation range to mark that this is a macro argument instantation + // rather than a normal one. + return create(SL, IL, SourceLocation()); + } }; /// SLocEntry - This is a discriminated union of FileInfo and @@ -500,8 +539,8 @@ public: //===--------------------------------------------------------------------===// /// createFileID - Create a new FileID that represents the specified file - /// being #included from the specified IncludePosition. This returns 0 on - /// error and translates NULL into standard input. + /// being #included from the specified IncludePosition. This translates NULL + /// into standard input. /// PreallocateID should be non-zero to specify which pre-allocated, /// lazily computed source location is being filled in by this operation. FileID createFileID(const FileEntry *SourceFile, SourceLocation IncludePos, @@ -532,9 +571,17 @@ public: return MainFileID; } + /// createMacroArgInstantiationLoc - Return a new SourceLocation that encodes + /// the fact that a token from SpellingLoc should actually be referenced from + /// InstantiationLoc, and that it represents the instantiation of a macro + /// argument into the function-like macro body. + SourceLocation createMacroArgInstantiationLoc(SourceLocation Loc, + SourceLocation InstantiationLoc, + unsigned TokLength); + /// createInstantiationLoc - Return a new SourceLocation that encodes the fact - /// that a token at Loc should actually be referenced from InstantiationLoc. - /// TokLength is the length of the token being instantiated. + /// that a token from SpellingLoc should actually be referenced from + /// InstantiationLoc. SourceLocation createInstantiationLoc(SourceLocation Loc, SourceLocation InstantiationLocStart, SourceLocation InstantiationLocEnd, @@ -721,7 +768,7 @@ public: if (Loc.isFileID()) return std::make_pair(FID, Offset); - return getDecomposedInstantiationLocSlowCase(E, Offset); + return getDecomposedInstantiationLocSlowCase(E); } /// getDecomposedSpellingLoc - Decompose the specified location into a raw @@ -745,6 +792,12 @@ public: return getDecomposedLoc(SpellingLoc).second; } + /// isMacroArgInstantiation - This method tests whether the given source + /// location represents a macro argument's instantiation into the + /// function-like macro definition. Such source locations only appear inside + /// of the instantiation locations representing where a particular + /// function-like macro was expanded. + bool isMacroArgInstantiation(SourceLocation Loc) const; //===--------------------------------------------------------------------===// // Queries about the code at a SourceLocation. @@ -831,13 +884,38 @@ public: return getFileCharacteristic(Loc) == SrcMgr::C_ExternCSystem; } - /// \brief Returns true if the given MacroID location points at the first - /// token of the macro instantiation. - bool isAtStartOfMacroInstantiation(SourceLocation Loc) const; + /// \brief Given a specific chunk of a FileID (FileID with offset+length), + /// returns true if \arg Loc is inside that chunk and sets relative offset + /// (offset of \arg Loc from beginning of chunk) to \arg relativeOffset. + bool isInFileID(SourceLocation Loc, + FileID FID, unsigned offset, unsigned length, + unsigned *relativeOffset = 0) const { + assert(!FID.isInvalid()); + if (Loc.isInvalid()) + return false; + + unsigned start = getSLocEntry(FID).getOffset() + offset; + unsigned end = start + length; + +#ifndef NDEBUG + // Make sure offset/length describe a chunk inside the given FileID. + unsigned NextOffset; + if (FID.ID+1 == SLocEntryTable.size()) + NextOffset = getNextOffset(); + else + NextOffset = getSLocEntry(FID.ID+1).getOffset(); + assert(start < NextOffset); + assert(end < NextOffset); +#endif - /// \brief Returns true if the given MacroID location points at the last - /// token of the macro instantiation. - bool isAtEndOfMacroInstantiation(SourceLocation Loc) const; + if (Loc.getOffset() >= start && Loc.getOffset() < end) { + if (relativeOffset) + *relativeOffset = Loc.getOffset() - start; + return true; + } + + return false; + } //===--------------------------------------------------------------------===// // Line Table Manipulation Routines @@ -845,7 +923,7 @@ public: /// getLineTableFilenameID - Return the uniqued ID for the specified filename. /// - unsigned getLineTableFilenameID(const char *Ptr, unsigned Len); + unsigned getLineTableFilenameID(llvm::StringRef Str); /// AddLineNote - Add a line note to the line table for the FileID and offset /// specified by Loc. If FilenameID is -1, it is considered to be @@ -899,6 +977,19 @@ public: /// \returns true if LHS source location comes before RHS, false otherwise. bool isBeforeInTranslationUnit(SourceLocation LHS, SourceLocation RHS) const; + /// \brief Determines the order of 2 source locations in the "source location + /// address space". + static bool isBeforeInSourceLocationOffset(SourceLocation LHS, + SourceLocation RHS) { + return isBeforeInSourceLocationOffset(LHS, RHS.getOffset()); + } + + /// \brief Determines the order of a source location and a source location + /// offset in the "source location address space". + static bool isBeforeInSourceLocationOffset(SourceLocation LHS, unsigned RHS) { + return LHS.getOffset() < RHS; + } + // Iterators over FileInfos. typedef llvm::DenseMap<const FileEntry*, SrcMgr::ContentCache*> ::const_iterator fileinfo_iterator; @@ -952,6 +1043,14 @@ public: private: const llvm::MemoryBuffer *getFakeBufferForRecovery() const; + /// createInstantiationLoc - Implements the common elements of storing an + /// instantiation info struct into the SLocEntry table and producing a source + /// location that refers to it. + SourceLocation createInstantiationLocImpl(const SrcMgr::InstantiationInfo &II, + unsigned TokLength, + unsigned PreallocatedID = 0, + unsigned Offset = 0); + /// isOffsetInFileID - Return true if the specified FileID contains the /// specified SourceLocation offset. This is a very hot method. inline bool isOffsetInFileID(FileID FID, unsigned SLocOffset) const { @@ -989,8 +1088,7 @@ private: SourceLocation getSpellingLocSlowCase(SourceLocation Loc) const; std::pair<FileID, unsigned> - getDecomposedInstantiationLocSlowCase(const SrcMgr::SLocEntry *E, - unsigned Offset) const; + getDecomposedInstantiationLocSlowCase(const SrcMgr::SLocEntry *E) const; std::pair<FileID, unsigned> getDecomposedSpellingLocSlowCase(const SrcMgr::SLocEntry *E, unsigned Offset) const; diff --git a/include/clang/Basic/SourceManagerInternals.h b/include/clang/Basic/SourceManagerInternals.h index 258989cb7787..3f5d1a35e595 100644 --- a/include/clang/Basic/SourceManagerInternals.h +++ b/include/clang/Basic/SourceManagerInternals.h @@ -97,7 +97,7 @@ public: ~LineTableInfo() {} - unsigned getLineTableFilenameID(const char *Ptr, unsigned Len); + unsigned getLineTableFilenameID(llvm::StringRef Str); const char *getFilename(unsigned ID) const { assert(ID < FilenamesByID.size() && "Invalid FilenameID"); return FilenamesByID[ID]->getKeyData(); diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h index d21bda7d9a2e..cfce0ccbc9dd 100644 --- a/include/clang/Basic/Specifiers.h +++ b/include/clang/Basic/Specifiers.h @@ -83,7 +83,7 @@ namespace clang { /// ExprValueKind - The categorization of expression values, /// currently following the C++0x scheme. enum ExprValueKind { - /// An r-value expression (a gr-value in the C++0x taxonomy) + /// An r-value expression (a pr-value in the C++0x taxonomy) /// produces a temporary value. VK_RValue, diff --git a/include/clang/Basic/StmtNodes.td b/include/clang/Basic/StmtNodes.td index 03f4cc3ec6d8..73996e43d5da 100644 --- a/include/clang/Basic/StmtNodes.td +++ b/include/clang/Basic/StmtNodes.td @@ -37,6 +37,7 @@ def ObjCAtFinallyStmt : Stmt; def ObjCAtThrowStmt : Stmt; def ObjCAtSynchronizedStmt : Stmt; def ObjCForCollectionStmt : Stmt; +def ObjCAutoreleasePoolStmt : Stmt; // C++ statments def CXXCatchStmt : Stmt; @@ -119,7 +120,9 @@ def UnresolvedMemberExpr : DStmt<OverloadExpr>; def CXXNoexceptExpr : DStmt<Expr>; def PackExpansionExpr : DStmt<Expr>; def SizeOfPackExpr : DStmt<Expr>; +def SubstNonTypeTemplateParmExpr : DStmt<Expr>; def SubstNonTypeTemplateParmPackExpr : DStmt<Expr>; +def MaterializeTemporaryExpr : DStmt<Expr>; // Obj-C Expressions. def ObjCStringLiteral : DStmt<Expr>; @@ -130,6 +133,10 @@ def ObjCProtocolExpr : DStmt<Expr>; def ObjCIvarRefExpr : DStmt<Expr>; def ObjCPropertyRefExpr : DStmt<Expr>; def ObjCIsaExpr : DStmt<Expr>; +def ObjCIndirectCopyRestoreExpr : DStmt<Expr>; + +// Obj-C ARC Expressions. +def ObjCBridgedCastExpr : DStmt<ExplicitCastExpr>; // CUDA Expressions. def CUDAKernelCallExpr : DStmt<CallExpr>; diff --git a/include/clang/Basic/TargetInfo.h b/include/clang/Basic/TargetInfo.h index 76006d4292e8..4559cf2f64be 100644 --- a/include/clang/Basic/TargetInfo.h +++ b/include/clang/Basic/TargetInfo.h @@ -240,6 +240,14 @@ public: return getTypeWidth(IntMaxType); } + /// getRegisterWidth - Return the "preferred" register width on this target. + uint64_t getRegisterWidth() const { + // Currently we assume the register width on the target matches the pointer + // width, we can introduce a new variable for this if/when some target wants + // it. + return LongWidth; + } + /// getUserLabelPrefix - This returns the default value of the /// __USER_LABEL_PREFIX__ macro, which is the prefix given to user symbols by /// default. On most platforms this is "_", but it is "" on some, and "." on @@ -295,6 +303,11 @@ public: /// __builtin_va_list, which is target-specific. virtual const char *getVAListDeclaration() const = 0; + /// isValidClobber - Returns whether the passed in string is + /// a valid clobber in an inline asm statement. This is used by + /// Sema. + bool isValidClobber(llvm::StringRef Name) const; + /// isValidGCCRegisterName - Returns whether the passed in string /// is a valid register name according to GCC. This is used by Sema for /// inline asm statements. @@ -396,6 +409,11 @@ public: const char * const Register; }; + struct AddlRegName { + const char * const Names[5]; + const unsigned RegNum; + }; + virtual bool useGlobalsForAutomaticVariables() const { return false; } /// getCFStringSection - Return the section to use for CFString @@ -511,6 +529,7 @@ public: // getRegParmMax - Returns maximal number of args passed in registers. unsigned getRegParmMax() const { + assert(RegParmMax < 7 && "RegParmMax value is larger than AST can handle"); return RegParmMax; } @@ -566,6 +585,11 @@ protected: unsigned &NumNames) const = 0; virtual void getGCCRegAliases(const GCCRegAlias *&Aliases, unsigned &NumAliases) const = 0; + virtual void getGCCAddlRegNames(const AddlRegName *&Addl, + unsigned &NumAddl) const { + Addl = 0; + NumAddl = 0; + } virtual bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &info) const= 0; }; diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def index dfba7eec8ae7..86172b83ff42 100644 --- a/include/clang/Basic/TokenKinds.def +++ b/include/clang/Basic/TokenKinds.def @@ -422,6 +422,12 @@ KEYWORD(__pascal , KEYALL) KEYWORD(__vector , KEYALTIVEC) KEYWORD(__pixel , KEYALTIVEC) +// Objective-C ARC keywords. +KEYWORD(__bridge , KEYARC) +KEYWORD(__bridge_transfer , KEYARC) +KEYWORD(__bridge_retained , KEYARC) +KEYWORD(__bridge_retain , KEYARC) + // Alternate spelling for various tokens. There are GCC extensions in all // languages, but should not be disabled in strict conformance mode. ALIAS("__alignof__" , __alignof , KEYALL) @@ -507,6 +513,7 @@ OBJC1_AT_KEYWORD(try) OBJC1_AT_KEYWORD(catch) OBJC1_AT_KEYWORD(finally) OBJC1_AT_KEYWORD(synchronized) +OBJC1_AT_KEYWORD(autoreleasepool) OBJC2_AT_KEYWORD(property) OBJC2_AT_KEYWORD(package) diff --git a/include/clang/Basic/arm_neon.td b/include/clang/Basic/arm_neon.td index b3da12254a84..71a0aa2726c0 100644 --- a/include/clang/Basic/arm_neon.td +++ b/include/clang/Basic/arm_neon.td @@ -182,7 +182,7 @@ def VMAX : SInst<"vmax", "ddd", "csiUcUsUifQcQsQiQUcQUsQUiQf">; def VMIN : SInst<"vmin", "ddd", "csiUcUsUifQcQsQiQUcQUsQUiQf">; //////////////////////////////////////////////////////////////////////////////// -// E.3.7 Pairdise Addition +// E.3.7 Pairwise Addition def VPADD : IInst<"vpadd", "ddd", "csiUcUsUif">; def VPADDL : SInst<"vpaddl", "nd", "csiUcUsUiQcQsQiQUcQUsQUi">; def VPADAL : SInst<"vpadal", "nnd", "csiUcUsUiQcQsQiQUcQUsQUi">; @@ -352,7 +352,7 @@ def VEXT : WInst<"vext", "dddi", "cUcPcsUsPsiUilUlfQcQUcQPcQsQUsQPsQiQUiQlQUlQf">; //////////////////////////////////////////////////////////////////////////////// -// E.3.27 Reverse vector elements (sdap endianness) +// E.3.27 Reverse vector elements def VREV64 : Inst<"vrev64", "dd", "csiUcUsUiPcPsfQcQsQiQUcQUsQUiQPcQPsQf", OP_REV64>; def VREV32 : Inst<"vrev32", "dd", "csUcUsPcPsQcQsQUcQUsQPcQPs", OP_REV32>; diff --git a/include/clang/CodeGen/BackendUtil.h b/include/clang/CodeGen/BackendUtil.h index abcef8130dbc..9636d6e527c2 100644 --- a/include/clang/CodeGen/BackendUtil.h +++ b/include/clang/CodeGen/BackendUtil.h @@ -19,6 +19,7 @@ namespace clang { class Diagnostic; class CodeGenOptions; class TargetOptions; + class LangOptions; enum BackendAction { Backend_EmitAssembly, ///< Emit native assembly files @@ -30,7 +31,8 @@ namespace clang { }; void EmitBackendOutput(Diagnostic &Diags, const CodeGenOptions &CGOpts, - const TargetOptions &TOpts, llvm::Module *M, + const TargetOptions &TOpts, const LangOptions &LOpts, + llvm::Module *M, BackendAction Action, llvm::raw_ostream *OS); } diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td index 14d8f67b9de3..d8e9e3d3e88b 100644 --- a/include/clang/Driver/CC1Options.td +++ b/include/clang/Driver/CC1Options.td @@ -143,6 +143,8 @@ def femit_coverage_data: Flag<"-femit-coverage-data">, def coverage_file : Separate<"-coverage-file">, HelpText<"Emit coverage data to this filename. The extension will be replaced.">; def coverage_file_EQ : Joined<"-coverage-file=">, Alias<coverage_file>; +def fuse_register_sized_bitfield_access: Flag<"-fuse-register-sized-bitfield-access">, + HelpText<"Use register sized accesses to bit-fields, when possible.">; def relaxed_aliasing : Flag<"-relaxed-aliasing">, HelpText<"Turn off Type Based Alias Analysis">; def masm_verbose : Flag<"-masm-verbose">, @@ -157,6 +159,8 @@ def mfloat_abi : Separate<"-mfloat-abi">, HelpText<"The float ABI to use">; def mlimit_float_precision : Separate<"-mlimit-float-precision">, HelpText<"Limit float precision to the given value">; +def mno_exec_stack : Flag<"-mnoexecstack">, + HelpText<"Mark the file as not needing an executable stack">; def mno_zero_initialized_in_bss : Flag<"-mno-zero-initialized-in-bss">, HelpText<"Do not put zero initialized data in the BSS">; def momit_leaf_frame_pointer : Flag<"-momit-leaf-frame-pointer">, @@ -204,6 +208,7 @@ def MQ : Separate<"-MQ">, HelpText<"Specify target to quote for dependency">; def MT : Separate<"-MT">, HelpText<"Specify target for dependency">; def MP : Flag<"-MP">, HelpText<"Create phony target for each dependency (other than main file)">; +def MG : Flag<"-MG">, HelpText<"Add missing headers to dependency list">; //===----------------------------------------------------------------------===// // Diagnostic Options @@ -259,7 +264,7 @@ def ftabstop : Separate<"-ftabstop">, MetaVarName<"<N>">, def ferror_limit : Separate<"-ferror-limit">, MetaVarName<"<N>">, HelpText<"Set the maximum number of errors to emit before stopping (0 = no limit).">; def fmacro_backtrace_limit : Separate<"-fmacro-backtrace-limit">, MetaVarName<"<N>">, - HelpText<"Set the maximum number of entries to print in a macro instantiation backtrace (0 = no limit).">; + HelpText<"Set the maximum number of entries to print in a macro expansion backtrace (0 = no limit).">; def ftemplate_backtrace_limit : Separate<"-ftemplate-backtrace-limit">, MetaVarName<"<N>">, HelpText<"Set the maximum number of entries to print in a template instantiation backtrace (0 = no limit).">; def fmessage_length : Separate<"-fmessage-length">, MetaVarName<"<N>">, @@ -352,8 +357,6 @@ def ast_dump_xml : Flag<"-ast-dump-xml">, HelpText<"Build ASTs and then debug dump them in a verbose XML format">; def ast_view : Flag<"-ast-view">, HelpText<"Build ASTs and view them with GraphViz">; -def boostcon : Flag<"-boostcon">, - HelpText<"BoostCon workshop mode">; def print_decl_contexts : Flag<"-print-decl-contexts">, HelpText<"Print DeclContexts and their Decls">; def emit_pth : Flag<"-emit-pth">, @@ -383,6 +386,15 @@ def create_module : Flag<"-create-module">, HelpText<"Create a module definition file">; } +def arcmt_check : Flag<"-arcmt-check">, + HelpText<"Check for ARC migration issues that need manual handling">; +def arcmt_modify : Flag<"-arcmt-modify">, + HelpText<"Apply modifications to files to conform to ARC">; +def arcmt_migrate : Flag<"-arcmt-migrate">, + HelpText<"Apply modifications and produces temporary files that conform to ARC">; +def arcmt_migrate_directory : Separate<"-arcmt-migrate-directory">, + HelpText<"Directory for temporary files produced during ARC migration">; + def import_module : Separate<"-import-module">, HelpText<"Import a module definition file">; @@ -452,6 +464,8 @@ def fhidden_weak_vtables : Flag<"-fhidden-weak-vtables">, HelpText<"Generate weak vtables and RTTI with hidden visibility">; def std_EQ : Joined<"-std=">, HelpText<"Language standard to compile for">; +def stdlib_EQ : Joined<"-stdlib=">, + HelpText<"C++ standard library to use">; def fmath_errno : Flag<"-fmath-errno">, HelpText<"Require math functions to indicate errors by setting errno">; def fms_extensions : Flag<"-fms-extensions">, @@ -479,6 +493,18 @@ def fconstant_string_class : Separate<"-fconstant-string-class">, HelpText<"Specify the class to use for constant Objective-C string objects.">; def fno_constant_cfstrings : Flag<"-fno-constant-cfstrings">, HelpText<"Enable creation of CodeFoundation-type constant strings">; +def fobjc_arc : Flag<"-fobjc-arc">, + HelpText<"Synthesize retain and release calls for Objective-C pointers">; +def fobjc_arc_cxxlib_EQ : Joined<"-fobjc-arc-cxxlib=">, + HelpText<"Objective-C++ Automatic Reference Counting standard library kind">; +def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">, + HelpText<"Use EH-safe code when synthesizing retains and releases in -fobjc-arc">; +def fobjc_runtime_has_arc : Flag<"-fobjc-runtime-has-arc">, + HelpText<"The target Objective-C runtime provides ARC entrypoints">; +def fobjc_runtime_has_weak : Flag<"-fobjc-runtime-has-weak">, + HelpText<"The target Objective-C runtime supports ARC weak operations">; +def fobjc_runtime_has_terminate : Flag<"-fobjc-runtime-has-terminate">, + HelpText<"The target Objective-C runtime provides an objc_terminate entrypoint">; def fobjc_gc : Flag<"-fobjc-gc">, HelpText<"Enable Objective-C garbage collection">; def fobjc_gc_only : Flag<"-fobjc-gc-only">, @@ -493,8 +519,10 @@ def print_ivar_layout : Flag<"-print-ivar-layout">, HelpText<"Enable Objective-C Ivar layout bitmap print trace">; def fobjc_nonfragile_abi : Flag<"-fobjc-nonfragile-abi">, HelpText<"enable objective-c's nonfragile abi">; -def fobjc_infer_related_result_type : Flag<"-fobjc-infer-related-result-type">, - HelpText<"infer Objective-C related result type based on method family">; +def fno_objc_infer_related_result_type : Flag< + "-fno-objc-infer-related-result-type">, + HelpText< + "do not infer Objective-C related result type based on method family">; def ftrapv : Flag<"-ftrapv">, HelpText<"Trap on integer overflow">; def ftrapv_handler : Separate<"-ftrapv-handler">, @@ -554,6 +582,8 @@ def fdelayed_template_parsing : Flag<"-fdelayed-template-parsing">, "translation unit ">; def funknown_anytype : Flag<"-funknown-anytype">, HelpText<"Enable parser support for the __unknown_anytype type; for testing purposes only">; +def fdebugger_support : Flag<"-fdebugger-support">, + HelpText<"Enable special debugger support behavior">; def fdeprecated_macro : Flag<"-fdeprecated-macro">, HelpText<"Defines the __DEPRECATED macro">; def fno_deprecated_macro : Flag<"-fno-deprecated-macro">, diff --git a/include/clang/Driver/Compilation.h b/include/clang/Driver/Compilation.h index 22d6b4ec6c8f..2db712d9321f 100644 --- a/include/clang/Driver/Compilation.h +++ b/include/clang/Driver/Compilation.h @@ -14,7 +14,6 @@ #include "clang/Driver/Util.h" #include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/SmallVector.h" namespace llvm { class raw_ostream; diff --git a/include/clang/Driver/Driver.h b/include/clang/Driver/Driver.h index 5a7d830b069d..b6951663148b 100644 --- a/include/clang/Driver/Driver.h +++ b/include/clang/Driver/Driver.h @@ -357,6 +357,8 @@ public: bool ShouldUseClangCompiler(const Compilation &C, const JobAction &JA, const llvm::Triple &ArchName) const; + bool IsUsingLTO(const ArgList &Args) const; + /// @} /// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and diff --git a/include/clang/Driver/ObjCRuntime.h b/include/clang/Driver/ObjCRuntime.h new file mode 100644 index 000000000000..55164604338d --- /dev/null +++ b/include/clang/Driver/ObjCRuntime.h @@ -0,0 +1,46 @@ +//===--- ObjCRuntime.h - Objective C runtime features -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_DRIVER_OBJCRUNTIME_H_ +#define CLANG_DRIVER_OBJCRUNTIME_H_ + +namespace clang { +namespace driver { + +class ObjCRuntime { +public: + enum Kind { GNU, NeXT }; +private: + unsigned RuntimeKind : 1; +public: + void setKind(Kind k) { RuntimeKind = k; } + Kind getKind() const { return static_cast<Kind>(RuntimeKind); } + + /// True if the runtime provides native ARC entrypoints. ARC may + /// still be usable without this if the tool-chain provides a + /// statically-linked runtime support library. + unsigned HasARC : 1; + + /// True if the runtime supports ARC zeroing __weak. + unsigned HasWeak : 1; + + /// True if the runtime provides the following entrypoint: + /// void objc_terminate(void); + /// If available, this will be called instead of abort() when an + /// exception is thrown out of an EH cleanup. + unsigned HasTerminate : 1; + + ObjCRuntime() : RuntimeKind(NeXT), HasARC(false), HasWeak(false), + HasTerminate(false) {} +}; + +} +} + +#endif diff --git a/include/clang/Driver/Option.h b/include/clang/Driver/Option.h index 9625465f48f4..9dfa4614009f 100644 --- a/include/clang/Driver/Option.h +++ b/include/clang/Driver/Option.h @@ -11,6 +11,7 @@ #define CLANG_DRIVER_OPTION_H_ #include "clang/Driver/OptSpecifier.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" using llvm::isa; using llvm::cast; @@ -64,7 +65,7 @@ namespace driver { OptSpecifier ID; /// The option name. - const char *Name; + llvm::StringRef Name; /// Group this option is a member of, if any. const OptionGroup *Group; @@ -103,7 +104,7 @@ namespace driver { unsigned getID() const { return ID.getID(); } OptionClass getKind() const { return Kind; } - const char *getName() const { return Name; } + llvm::StringRef getName() const { return Name; } const OptionGroup *getGroup() const { return Group; } const Option *getAlias() const { return Alias; } @@ -143,7 +144,7 @@ namespace driver { /// getRenderName - Return the name to use when rendering this /// option. - const char *getRenderName() const { + llvm::StringRef getRenderName() const { return getUnaliasedOption()->getName(); } diff --git a/include/clang/Driver/Options.td b/include/clang/Driver/Options.td index 72039766d907..d5482765e756 100644 --- a/include/clang/Driver/Options.td +++ b/include/clang/Driver/Options.td @@ -112,6 +112,17 @@ def ccc_print_phases : Flag<"-ccc-print-phases">, CCCDebugOpt, def ccc_print_bindings : Flag<"-ccc-print-bindings">, CCCDebugOpt, HelpText<"Show bindings of tools to actions">; +def ccc_arcmt_check : Flag<"-ccc-arcmt-check">, CCCDriverOpt, + HelpText<"Check for ARC migration issues that need manual handling">; +def ccc_arcmt_modify : Flag<"-ccc-arcmt-modify">, CCCDriverOpt, + HelpText<"Apply modifications to files to conform to ARC">; +def ccc_arrmt_check : Flag<"-ccc-arrmt-check">, Alias<ccc_arcmt_check>; +def ccc_arrmt_modify : Flag<"-ccc-arrmt-modify">, Alias<ccc_arcmt_modify>; +def ccc_arcmt_migrate : Separate<"-ccc-arcmt-migrate">, CCCDriverOpt, + HelpText<"Apply modifications and produces temporary files that conform to ARC">; +def ccc_arcmt_migrate_EQ : Joined<"-ccc-arcmt-migrate=">, CCCDriverOpt, + Alias<ccc_arcmt_migrate>; + // Make sure all other -ccc- options are rejected. def ccc_ : Joined<"-ccc-">, Group<ccc_Group>, Flags<[Unsupported]>; @@ -382,6 +393,10 @@ def fno_verbose_asm : Flag<"-fno-verbose-asm">, Group<f_Group>; def fno_working_directory : Flag<"-fno-working-directory">, Group<f_Group>; def fno_wrapv : Flag<"-fno-wrapv">, Group<f_Group>; def fno_zero_initialized_in_bss : Flag<"-fno-zero-initialized-in-bss">, Group<f_Group>; +def fobjc_arc : Flag<"-fobjc-arc">, Group<f_Group>; +def fno_objc_arc : Flag<"-fno-objc-arc">, Group<f_Group>; +def fobjc_arc_exceptions : Flag<"-fobjc-arc-exceptions">, Group<f_Group>; +def fno_objc_arc_exceptions : Flag<"-fno-objc-arc-exceptions">, Group<f_Group>; def fobjc_atdefs : Flag<"-fobjc-atdefs">, Group<clang_ignored_f_Group>; def fobjc_call_cxx_cdtors : Flag<"-fobjc-call-cxx-cdtors">, Group<clang_ignored_f_Group>; def fobjc_default_synthesize_properties : @@ -409,6 +424,7 @@ def fomit_frame_pointer : Flag<"-fomit-frame-pointer">, Group<f_Group>; def fopenmp : Flag<"-fopenmp">, Group<f_Group>; def force__cpusubtype__ALL : Flag<"-force_cpusubtype_ALL">; def force__flat__namespace : Flag<"-force_flat_namespace">; +def force__load : Separate<"-force_load">; def foutput_class_dir_EQ : Joined<"-foutput-class-dir=">, Group<f_Group>; def fpascal_strings : Flag<"-fpascal-strings">, Group<f_Group>; def fpch_preprocess : Flag<"-fpch-preprocess">, Group<f_Group>; @@ -803,8 +819,8 @@ def _specs : Separate<"--specs">, Alias<specs_EQ>; def _static : Flag<"--static">, Alias<static>; def _std_EQ : Joined<"--std=">, Alias<std_EQ>; def _std : Separate<"--std">, Alias<std_EQ>; -def _stdlib_EQ : Joined<"--stdlib=">, Alias<std_EQ>; -def _stdlib : Separate<"--stdlib">, Alias<std_EQ>; +def _stdlib_EQ : Joined<"--stdlib=">, Alias<stdlib_EQ>; +def _stdlib : Separate<"--stdlib">, Alias<stdlib_EQ>; def _sysroot_EQ : Joined<"--sysroot=">; def _sysroot : Separate<"--sysroot">, Alias<_sysroot_EQ>; def _target_help : Flag<"--target-help">; diff --git a/include/clang/Driver/ToolChain.h b/include/clang/Driver/ToolChain.h index 626d54c37b8a..4836d3ffac22 100644 --- a/include/clang/Driver/ToolChain.h +++ b/include/clang/Driver/ToolChain.h @@ -26,6 +26,7 @@ namespace driver { class HostInfo; class InputArgList; class JobAction; + class ObjCRuntime; class Tool; /// ToolChain - Access to tools for a single platform. @@ -177,6 +178,12 @@ public: /// Clang. virtual std::string ComputeEffectiveClangTriple(const ArgList &Args) const; + /// configureObjCRuntime - Configure the known properties of the + /// Objective-C runtime for this platform. + /// + /// FIXME: this doesn't really belong here. + virtual void configureObjCRuntime(ObjCRuntime &runtime) const; + // GetCXXStdlibType - Determine the C++ standard library type to use with the // given compilation arguments. virtual CXXStdlibType GetCXXStdlibType(const ArgList &Args) const; @@ -184,7 +191,8 @@ public: /// AddClangCXXStdlibIncludeArgs - Add the clang -cc1 level arguments to set /// the include paths to use for the given C++ standard library type. virtual void AddClangCXXStdlibIncludeArgs(const ArgList &Args, - ArgStringList &CmdArgs) const; + ArgStringList &CmdArgs, + bool ObjCXXAutoRefCount) const; /// AddCXXStdlibLibArgs - Add the system specific linker arguments to use /// for the given C++ standard library type. diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index 339297eb9362..58a60a1f9d87 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -249,9 +249,9 @@ private: /// \brief Whether we should be caching code-completion results. bool ShouldCacheCodeCompletionResults; - /// \brief Whether we want to include nested macro instantiations in the + /// \brief Whether we want to include nested macro expansions in the /// detailed preprocessing record. - bool NestedMacroInstantiations; + bool NestedMacroExpansions; static void ConfigureDiags(llvm::IntrusiveRefCntPtr<Diagnostic> &Diags, const char **ArgBegin, const char **ArgEnd, @@ -363,7 +363,7 @@ private: unsigned MaxLines, bool &CreatedBuffer); llvm::MemoryBuffer *getMainBufferWithPrecompiledPreamble( - CompilerInvocation PreambleInvocation, + const CompilerInvocation &PreambleInvocationIn, bool AllowRebuild = true, unsigned MaxLines = 0); void RealizeTopLevelDeclsFromPreamble(); @@ -612,7 +612,7 @@ public: bool PrecompilePreamble = false, bool CompleteTranslationUnit = true, bool CacheCodeCompletionResults = false, - bool NestedMacroInstantiations = true); + bool NestedMacroExpansions = true); /// LoadFromCommandLine - Create an ASTUnit from a vector of command line /// arguments, which must specify exactly one source file. @@ -642,7 +642,7 @@ public: bool CacheCodeCompletionResults = false, bool CXXPrecompilePreamble = false, bool CXXChainedPCH = false, - bool NestedMacroInstantiations = true); + bool NestedMacroExpansions = true); /// \brief Reparse the source files using the same command-line options that /// were originally used to produce this translation unit. @@ -680,8 +680,8 @@ public: /// \brief Save this translation unit to a file with the given name. /// - /// \returns True if an error occurred, false otherwise. - bool Save(llvm::StringRef File); + /// \returns An indication of whether the save was successful or not. + CXSaveError Save(llvm::StringRef File); /// \brief Serialize this translation unit with the given output stream. /// diff --git a/include/clang/Frontend/CodeGenOptions.h b/include/clang/Frontend/CodeGenOptions.h index 1c686c76df5f..5d040b4f266c 100644 --- a/include/clang/Frontend/CodeGenOptions.h +++ b/include/clang/Frontend/CodeGenOptions.h @@ -36,6 +36,7 @@ public: }; unsigned AsmVerbose : 1; /// -dA, -fverbose-asm. + unsigned ObjCAutoRefCountExceptions : 1; /// Whether ARC should be EH-safe. unsigned CXAAtExit : 1; /// Use __cxa_atexit for calling destructors. unsigned CXXCtorDtorAliases: 1; /// Emit complete ctors/dtors as linker /// aliases to base ctors when possible. @@ -69,11 +70,14 @@ public: unsigned MergeAllConstants : 1; /// Merge identical constants. unsigned NoCommon : 1; /// Set when -fno-common or C++ is enabled. unsigned NoDwarf2CFIAsm : 1; /// Set when -fno-dwarf2-cfi-asm is enabled. + unsigned NoExecStack : 1; /// Set when -Wa,--noexecstack is enabled. unsigned NoImplicitFloat : 1; /// Set when -mno-implicit-float is enabled. unsigned NoInfsFPMath : 1; /// Assume FP arguments, results not +-Inf. unsigned NoNaNsFPMath : 1; /// Assume FP arguments, results not NaN. unsigned NoZeroInitializedInBSS : 1; /// -fno-zero-initialized-in-bss unsigned ObjCDispatchMethod : 2; /// Method of Objective-C dispatch to use. + unsigned ObjCRuntimeHasARC : 1; /// The target runtime supports ARC natively + unsigned ObjCRuntimeHasTerminate : 1; /// The ObjC runtime has objc_terminate unsigned OmitLeafFramePointer : 1; /// Set when -momit-leaf-frame-pointer is /// enabled. unsigned OptimizationLevel : 3; /// The -O[0-4] option specified. @@ -89,6 +93,11 @@ public: unsigned UnrollLoops : 1; /// Control whether loops are unrolled. unsigned UnsafeFPMath : 1; /// Allow unsafe floating point optzns. unsigned UnwindTables : 1; /// Emit unwind tables. + + /// Attempt to use register sized accesses to bit-fields in structures, when + /// possible. + unsigned UseRegisterSizedBitfieldAccess : 1; + unsigned VerifyModule : 1; /// Control whether the module should be run /// through the LLVM Verifier. @@ -159,7 +168,10 @@ public: NoNaNsFPMath = 0; NoZeroInitializedInBSS = 0; NumRegisterParameters = 0; + ObjCAutoRefCountExceptions = 0; ObjCDispatchMethod = Legacy; + ObjCRuntimeHasARC = 0; + ObjCRuntimeHasTerminate = 0; OmitLeafFramePointer = 0; OptimizationLevel = 0; OptimizeSize = 0; @@ -173,6 +185,7 @@ public: UnrollLoops = 0; UnsafeFPMath = 0; UnwindTables = 0; + UseRegisterSizedBitfieldAccess = 0; VerifyModule = 1; Inlining = NoInlining; diff --git a/include/clang/Frontend/DependencyOutputOptions.h b/include/clang/Frontend/DependencyOutputOptions.h index 35aa6c6aace6..1e22c227fcea 100644 --- a/include/clang/Frontend/DependencyOutputOptions.h +++ b/include/clang/Frontend/DependencyOutputOptions.h @@ -24,6 +24,7 @@ public: unsigned UsePhonyTargets : 1; ///< Include phony targets for each /// dependency, which can avoid some 'make' /// problems. + unsigned AddMissingHeaderDeps : 1; ///< Add missing headers to dependency list /// The file to write dependency output to. std::string OutputFile; @@ -43,6 +44,7 @@ public: IncludeSystemHeaders = 0; ShowHeaderIncludes = 0; UsePhonyTargets = 0; + AddMissingHeaderDeps = 0; } }; diff --git a/include/clang/Frontend/DiagnosticOptions.h b/include/clang/Frontend/DiagnosticOptions.h index 56093c3c950c..5ae8eb367788 100644 --- a/include/clang/Frontend/DiagnosticOptions.h +++ b/include/clang/Frontend/DiagnosticOptions.h @@ -49,8 +49,7 @@ public: /// input source file. unsigned ErrorLimit; /// Limit # errors emitted. - unsigned MacroBacktraceLimit; /// Limit depth of macro instantiation - /// backtrace. + unsigned MacroBacktraceLimit; /// Limit depth of macro expansion backtrace. unsigned TemplateBacktraceLimit; /// Limit depth of instantiation backtrace. /// The distance between tab stops. diff --git a/include/clang/Frontend/FrontendAction.h b/include/clang/Frontend/FrontendAction.h index ee0863a4776f..f335475665fd 100644 --- a/include/clang/Frontend/FrontendAction.h +++ b/include/clang/Frontend/FrontendAction.h @@ -51,6 +51,7 @@ class FrontendAction { llvm::OwningPtr<ASTUnit> CurrentASTUnit; CompilerInstance *Instance; friend class ASTMergeAction; + friend class WrapperFrontendAction; private: ASTConsumer* CreateWrappedASTConsumer(CompilerInstance &CI, @@ -77,6 +78,14 @@ protected: virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) = 0; + /// \brief Callback before starting processing a single input, giving the + /// opportunity to modify the CompilerInvocation or do some other action + /// before BeginSourceFileAction is called. + /// + /// \return True on success; on failure \see BeginSourceFileAction() and + /// ExecutionAction() and EndSourceFileAction() will not be called. + virtual bool BeginInvocation(CompilerInstance &CI) { return true; } + /// BeginSourceFileAction - Callback at the start of processing a single /// input. /// @@ -253,6 +262,36 @@ public: virtual bool usesPreprocessorOnly() const { return true; } }; +/// WrapperFrontendAction - A frontend action which simply wraps some other +/// runtime specified frontend action. Deriving from this class allows an +/// action to inject custom logic around some existing action's behavior. It +/// implements every virtual method in the FrontendAction interface by +/// forwarding to the wrapped action. +class WrapperFrontendAction : public FrontendAction { + llvm::OwningPtr<FrontendAction> WrappedAction; + +protected: + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile); + virtual bool BeginInvocation(CompilerInstance &CI); + virtual bool BeginSourceFileAction(CompilerInstance &CI, + llvm::StringRef Filename); + virtual void ExecuteAction(); + virtual void EndSourceFileAction(); + +public: + /// Construct a WrapperFrontendAction from an existing action, taking + /// ownership of it. + WrapperFrontendAction(FrontendAction *WrappedAction); + + virtual bool usesPreprocessorOnly() const; + virtual bool usesCompleteTranslationUnit(); + virtual bool hasPCHSupport() const; + virtual bool hasASTFileSupport() const; + virtual bool hasIRSupport() const; + virtual bool hasCodeCompletionSupport() const; +}; + } // end namespace clang #endif diff --git a/include/clang/Frontend/FrontendActions.h b/include/clang/Frontend/FrontendActions.h index 4e67449b8549..b409ad1e0963 100644 --- a/include/clang/Frontend/FrontendActions.h +++ b/include/clang/Frontend/FrontendActions.h @@ -97,12 +97,6 @@ public: virtual bool hasCodeCompletionSupport() const { return true; } }; -class BoostConAction : public SyntaxOnlyAction { -protected: - virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, - llvm::StringRef InFile); -}; - /** * \brief Frontend action adaptor that merges ASTs together. * diff --git a/include/clang/Frontend/FrontendOptions.h b/include/clang/Frontend/FrontendOptions.h index 02f6f868fe49..225a955b4ac6 100644 --- a/include/clang/Frontend/FrontendOptions.h +++ b/include/clang/Frontend/FrontendOptions.h @@ -24,7 +24,6 @@ namespace frontend { ASTDumpXML, ///< Parse ASTs and dump them in XML. ASTPrint, ///< Parse ASTs and print them. ASTView, ///< Parse ASTs and view them in Graphviz. - BoostCon, ///< BoostCon mode. CreateModule, ///< Create module definition DumpRawTokens, ///< Dump out raw tokens. DumpTokens, ///< Dump out preprocessed tokens. @@ -77,6 +76,15 @@ public: unsigned FixWhatYouCan : 1; ///< Apply fixes even if there are /// unfixable errors. + enum { + ARCMT_None, + ARCMT_Check, + ARCMT_Modify, + ARCMT_Migrate + } ARCMTAction; + + std::string ARCMTMigrateDir; + /// The input files and their types. std::vector<std::pair<InputKind, std::string> > Inputs; @@ -131,6 +139,7 @@ public: ShowStats = 0; ShowTimers = 0; ShowVersion = 0; + ARCMTAction = ARCMT_None; } /// getInputKindForExtension - Return the appropriate input kind for a file diff --git a/include/clang/Frontend/HeaderSearchOptions.h b/include/clang/Frontend/HeaderSearchOptions.h index b0669eba4ccc..0347f98fd5ba 100644 --- a/include/clang/Frontend/HeaderSearchOptions.h +++ b/include/clang/Frontend/HeaderSearchOptions.h @@ -17,10 +17,12 @@ namespace clang { namespace frontend { /// IncludeDirGroup - Identifiers the group a include entry belongs to, which - /// represents its relative positive in the search list. + /// represents its relative positive in the search list. A #include of a "" + /// path starts at the -iquote group, then searches the Angled group, then + /// searches the system group, etc. enum IncludeDirGroup { - Quoted = 0, ///< `#include ""` paths. Thing `gcc -iquote`. - Angled, ///< Paths for both `#include ""` and `#include <>`. (`-I`) + Quoted = 0, ///< '#include ""' paths, added by'gcc -iquote'. + Angled, ///< Paths for '#include <>' added by '-I'. System, ///< Like Angled, but marks system directories. CXXSystem, ///< Like System, but only used for C++. After ///< Like System, but searched after the system directories. @@ -37,15 +39,15 @@ public: unsigned IsUserSupplied : 1; unsigned IsFramework : 1; - /// IsSysRootRelative - This is true if an absolute path should be treated - /// relative to the sysroot, or false if it should always be the absolute + /// IgnoreSysRoot - This is false if an absolute path should be treated + /// relative to the sysroot, or true if it should always be the absolute /// path. - unsigned IsSysRootRelative : 1; + unsigned IgnoreSysRoot : 1; Entry(llvm::StringRef path, frontend::IncludeDirGroup group, - bool isUserSupplied, bool isFramework, bool isSysRootRelative) + bool isUserSupplied, bool isFramework, bool ignoreSysRoot) : Path(path), Group(group), IsUserSupplied(isUserSupplied), - IsFramework(isFramework), IsSysRootRelative(isSysRootRelative) {} + IsFramework(isFramework), IgnoreSysRoot(ignoreSysRoot) {} }; /// If non-empty, the directory to use as a "virtual system root" for include @@ -80,20 +82,23 @@ public: /// Include the system standard C++ library include search directories. unsigned UseStandardCXXIncludes : 1; + /// Use libc++ instead of the default libstdc++. + unsigned UseLibcxx : 1; + /// Whether header search information should be output as for -v. unsigned Verbose : 1; public: HeaderSearchOptions(llvm::StringRef _Sysroot = "/") : Sysroot(_Sysroot), UseBuiltinIncludes(true), - UseStandardIncludes(true), UseStandardCXXIncludes(true), + UseStandardIncludes(true), UseStandardCXXIncludes(true), UseLibcxx(false), Verbose(false) {} /// AddPath - Add the \arg Path path to the specified \arg Group list. void AddPath(llvm::StringRef Path, frontend::IncludeDirGroup Group, - bool IsUserSupplied, bool IsFramework, bool IsSysRootRelative) { + bool IsUserSupplied, bool IsFramework, bool IgnoreSysRoot) { UserEntries.push_back(Entry(Path, Group, IsUserSupplied, IsFramework, - IsSysRootRelative)); + IgnoreSysRoot)); } }; diff --git a/include/clang/Frontend/PreprocessorOptions.h b/include/clang/Frontend/PreprocessorOptions.h index e471c5cf1d82..2e16c97e7d43 100644 --- a/include/clang/Frontend/PreprocessorOptions.h +++ b/include/clang/Frontend/PreprocessorOptions.h @@ -1,4 +1,4 @@ -//===--- PreprocessorOptionms.h ---------------------------------*- C++ -*-===// +//===--- PreprocessorOptions.h ----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -26,6 +26,15 @@ namespace clang { class Preprocessor; class LangOptions; +/// \brief Enumerate the kinds of standard library that +enum ObjCXXARCStandardLibraryKind { + ARCXX_nolib, + /// \brief libc++ + ARCXX_libcxx, + /// \brief libstdc++ + ARCXX_libstdcxx +}; + /// PreprocessorOptions - This class is used for passing the various options /// used in preprocessor initialization to InitializePreprocessor(). class PreprocessorOptions { @@ -39,11 +48,11 @@ public: unsigned DetailedRecord : 1; /// Whether we should maintain a detailed /// record of all macro definitions and - /// instantiations. + /// expansions. /// \brief Whether the detailed preprocessing record includes nested macro - /// instantiations. - unsigned DetailedRecordIncludesNestedMacroInstantiations : 1; + /// expansions. + unsigned DetailedRecordIncludesNestedMacroExpansions : 1; /// The implicit PCH included at the start of the translation unit, or empty. std::string ImplicitPCHInclude; @@ -104,6 +113,11 @@ public: /// compiler invocation and its buffers will be reused. bool RetainRemappedFileBuffers; + /// \brief The Objective-C++ ARC standard library that we should support, + /// by providing appropriate definitions to retrofit the standard library + /// with support for lifetime-qualified pointers. + ObjCXXARCStandardLibraryKind ObjCXXARCStandardLibrary; + typedef std::vector<std::pair<std::string, std::string> >::iterator remapped_file_iterator; typedef std::vector<std::pair<std::string, std::string> >::const_iterator @@ -140,12 +154,13 @@ public: public: PreprocessorOptions() : UsePredefines(true), DetailedRecord(false), - DetailedRecordIncludesNestedMacroInstantiations(true), + DetailedRecordIncludesNestedMacroExpansions(true), DisablePCHValidation(false), DisableStatCache(false), DumpDeserializedPCHDecls(false), PrecompiledPreambleBytes(0, true), RemappedFilesKeepOriginalName(true), - RetainRemappedFileBuffers(false) { } + RetainRemappedFileBuffers(false), + ObjCXXARCStandardLibrary(ARCXX_nolib) { } void addMacroDef(llvm::StringRef Name) { Macros.push_back(std::make_pair(Name, false)); diff --git a/include/clang/Frontend/TextDiagnosticPrinter.h b/include/clang/Frontend/TextDiagnosticPrinter.h index d7d2692cb547..79a9916cb435 100644 --- a/include/clang/Frontend/TextDiagnosticPrinter.h +++ b/include/clang/Frontend/TextDiagnosticPrinter.h @@ -62,18 +62,17 @@ public: std::string &CaretLine, const std::string &SourceLine); - void EmitCaretDiagnostic(Diagnostic::Level Level, SourceLocation Loc, - CharSourceRange *Ranges, unsigned NumRanges, - const SourceManager &SM, - const FixItHint *Hints, - unsigned NumHints, - unsigned Columns, - unsigned OnMacroInst, - unsigned MacroSkipStart, - unsigned MacroSkipEnd); - virtual void HandleDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &Info); + +private: + void EmitCaretDiagnostic(SourceLocation Loc, CharSourceRange *Ranges, + unsigned NumRanges, const SourceManager &SM, + const FixItHint *Hints, + unsigned NumHints, unsigned Columns, + unsigned OnMacroInst, unsigned MacroSkipStart, + unsigned MacroSkipEnd); + }; } // end namespace clang diff --git a/include/clang/Frontend/Utils.h b/include/clang/Frontend/Utils.h index 3c34c2dce24b..93d2c7d501e7 100644 --- a/include/clang/Frontend/Utils.h +++ b/include/clang/Frontend/Utils.h @@ -19,6 +19,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" #include "llvm/Support/raw_ostream.h" +#include "clang/Basic/Diagnostic.h" namespace llvm { class Triple; diff --git a/include/clang/Lex/Lexer.h b/include/clang/Lex/Lexer.h index 7c3d863bd3d3..f4297627e86f 100644 --- a/include/clang/Lex/Lexer.h +++ b/include/clang/Lex/Lexer.h @@ -95,8 +95,8 @@ public: /// _Pragma expansion. This has a variety of magic semantics that this method /// sets up. It returns a new'd Lexer that must be delete'd when done. static Lexer *Create_PragmaLexer(SourceLocation SpellingLoc, - SourceLocation InstantiationLocStart, - SourceLocation InstantiationLocEnd, + SourceLocation ExpansionLocStart, + SourceLocation ExpansionLocEnd, unsigned TokLen, Preprocessor &PP); @@ -241,8 +241,8 @@ public: /// is not necessary to copy any data, then the returned string may /// not point into the provided buffer. /// - /// This method lexes at the instantiation depth of the given - /// location and does not jump to the instantiation or spelling + /// This method lexes at the expansion depth of the given + /// location and does not jump to the expansion or spelling /// location. static llvm::StringRef getSpelling(SourceLocation loc, llvm::SmallVectorImpl<char> &buffer, @@ -293,7 +293,19 @@ public: static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &Features); - + + /// \brief Returns true if the given MacroID location points at the first + /// token of the macro expansion. + static bool isAtStartOfMacroExpansion(SourceLocation loc, + const SourceManager &SM, + const LangOptions &LangOpts); + + /// \brief Returns true if the given MacroID location points at the last + /// token of the macro expansion. + static bool isAtEndOfMacroExpansion(SourceLocation loc, + const SourceManager &SM, + const LangOptions &LangOpts); + /// \brief Compute the preamble of the given file. /// /// The preamble of a file contains the initial comments, include directives, diff --git a/include/clang/Lex/LiteralSupport.h b/include/clang/Lex/LiteralSupport.h index ec3d9c58d67e..0dbcd6d72d63 100644 --- a/include/clang/Lex/LiteralSupport.h +++ b/include/clang/Lex/LiteralSupport.h @@ -167,7 +167,9 @@ public: bool AnyWide; bool Pascal; - const char *GetString() { return ResultBuf.data(); } + llvm::StringRef GetString() const { + return llvm::StringRef(ResultBuf.data(), GetStringLength()); + } unsigned GetStringLength() const { return ResultPtr-ResultBuf.data(); } unsigned GetNumStringChars() const { diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h index 7c4cfb007233..9e9d7cf500a4 100644 --- a/include/clang/Lex/MacroInfo.h +++ b/include/clang/Lex/MacroInfo.h @@ -43,6 +43,10 @@ class MacroInfo { /// to. llvm::SmallVector<Token, 8> ReplacementTokens; + /// \brief Length in characters of the macro definition. + mutable unsigned DefinitionLength; + mutable bool IsDefinitionLengthCached : 1; + /// IsFunctionLike - True if this macro is a function-like macro, false if it /// is an object-like macro. bool IsFunctionLike : 1; @@ -116,6 +120,13 @@ public: /// getDefinitionEndLoc - Return the location of the last token in the macro. /// SourceLocation getDefinitionEndLoc() const { return EndLocation; } + + /// \brief Get length in characters of the macro definition. + unsigned getDefinitionLength(SourceManager &SM) const { + if (IsDefinitionLengthCached) + return DefinitionLength; + return getDefinitionLengthSlow(SM); + } /// isIdenticalTo - Return true if the specified macro definition is equal to /// this macro in spelling, arguments, and whitespace. This is used to emit @@ -232,6 +243,8 @@ public: /// AddTokenToBody - Add the specified token to the replacement text for the /// macro. void AddTokenToBody(const Token &Tok) { + assert(!IsDefinitionLengthCached && + "Changing replacement tokens after definition length got calculated"); ReplacementTokens.push_back(Tok); } @@ -248,6 +261,9 @@ public: assert(!IsDisabled && "Cannot disable an already-disabled macro!"); IsDisabled = true; } + +private: + unsigned getDefinitionLengthSlow(SourceManager &SM) const; }; } // end namespace clang diff --git a/include/clang/Lex/PPCallbacks.h b/include/clang/Lex/PPCallbacks.h index fd07a29f8e9d..a7948153a728 100644 --- a/include/clang/Lex/PPCallbacks.h +++ b/include/clang/Lex/PPCallbacks.h @@ -16,6 +16,7 @@ #include "clang/Lex/DirectoryLookup.h" #include "clang/Basic/SourceLocation.h" +#include "clang/Basic/DiagnosticIDs.h" #include "llvm/ADT/StringRef.h" #include <string> @@ -124,6 +125,24 @@ public: virtual void PragmaMessage(SourceLocation Loc, llvm::StringRef Str) { } + /// PragmaDiagnosticPush - This callback is invoked when a + /// #pragma gcc dianostic push directive is read. + virtual void PragmaDiagnosticPush(SourceLocation Loc, + llvm::StringRef Namespace) { + } + + /// PragmaDiagnosticPop - This callback is invoked when a + /// #pragma gcc dianostic pop directive is read. + virtual void PragmaDiagnosticPop(SourceLocation Loc, + llvm::StringRef Namespace) { + } + + /// PragmaDiagnostic - This callback is invoked when a + /// #pragma gcc dianostic directive is read. + virtual void PragmaDiagnostic(SourceLocation Loc, llvm::StringRef Namespace, + diag::Mapping mapping, llvm::StringRef Str) { + } + /// MacroExpands - This is called by /// Preprocessor::HandleMacroExpandedIdentifier when a macro invocation is /// found. @@ -232,6 +251,24 @@ public: Second->PragmaMessage(Loc, Str); } + virtual void PragmaDiagnosticPush(SourceLocation Loc, + llvm::StringRef Namespace) { + First->PragmaDiagnosticPush(Loc, Namespace); + Second->PragmaDiagnosticPush(Loc, Namespace); + } + + virtual void PragmaDiagnosticPop(SourceLocation Loc, + llvm::StringRef Namespace) { + First->PragmaDiagnosticPop(Loc, Namespace); + Second->PragmaDiagnosticPop(Loc, Namespace); + } + + virtual void PragmaDiagnostic(SourceLocation Loc, llvm::StringRef Namespace, + diag::Mapping mapping, llvm::StringRef Str) { + First->PragmaDiagnostic(Loc, Namespace, mapping, Str); + Second->PragmaDiagnostic(Loc, Namespace, mapping, Str); + } + virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo* MI) { First->MacroExpands(MacroNameTok, MI); Second->MacroExpands(MacroNameTok, MI); diff --git a/include/clang/Lex/PreprocessingRecord.h b/include/clang/Lex/PreprocessingRecord.h index e498e9d0a020..b38303a2f40b 100644 --- a/include/clang/Lex/PreprocessingRecord.h +++ b/include/clang/Lex/PreprocessingRecord.h @@ -38,13 +38,13 @@ namespace clang { class FileEntry; /// \brief Base class that describes a preprocessed entity, which may be a - /// preprocessor directive or macro instantiation. + /// preprocessor directive or macro expansion. class PreprocessedEntity { public: /// \brief The kind of preprocessed entity an object describes. enum EntityKind { - /// \brief A macro instantiation. - MacroInstantiationKind, + /// \brief A macro expansion. + MacroExpansionKind, /// \brief A preprocessing directive whose kind is not specified. /// @@ -110,31 +110,31 @@ namespace clang { void operator delete(void* data) throw(); }; - /// \brief Records the location of a macro instantiation. - class MacroInstantiation : public PreprocessedEntity { - /// \brief The name of the macro being instantiation. + /// \brief Records the location of a macro expansion. + class MacroExpansion : public PreprocessedEntity { + /// \brief The name of the macro being expanded. IdentifierInfo *Name; /// \brief The definition of this macro. MacroDefinition *Definition; public: - MacroInstantiation(IdentifierInfo *Name, SourceRange Range, - MacroDefinition *Definition) - : PreprocessedEntity(MacroInstantiationKind, Range), Name(Name), + MacroExpansion(IdentifierInfo *Name, SourceRange Range, + MacroDefinition *Definition) + : PreprocessedEntity(MacroExpansionKind, Range), Name(Name), Definition(Definition) { } - /// \brief The name of the macro being instantiated. + /// \brief The name of the macro being expanded. IdentifierInfo *getName() const { return Name; } - /// \brief The definition of the macro being instantiated. + /// \brief The definition of the macro being expanded. MacroDefinition *getDefinition() const { return Definition; } // Implement isa/cast/dyncast/etc. static bool classof(const PreprocessedEntity *PE) { - return PE->getKind() == MacroInstantiationKind; + return PE->getKind() == MacroExpansionKind; } - static bool classof(const MacroInstantiation *) { return true; } + static bool classof(const MacroExpansion *) { return true; } }; @@ -256,11 +256,11 @@ namespace clang { /// \brief A record of the steps taken while preprocessing a source file, /// including the various preprocessing directives processed, macros - /// instantiated, etc. + /// expanded, etc. class PreprocessingRecord : public PPCallbacks { - /// \brief Whether we should include nested macro instantiations in + /// \brief Whether we should include nested macro expansions in /// the preprocessing record. - bool IncludeNestedMacroInstantiations; + bool IncludeNestedMacroExpansions; /// \brief Allocator used to store preprocessing objects. llvm::BumpPtrAllocator BumpAlloc; @@ -286,7 +286,7 @@ namespace clang { public: /// \brief Construct - explicit PreprocessingRecord(bool IncludeNestedMacroInstantiations); + explicit PreprocessingRecord(bool IncludeNestedMacroExpansions); /// \brief Allocate memory in the preprocessing record. void *Allocate(unsigned Size, unsigned Align = 8) { diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 76e3f59ef4c4..f6f3205099a0 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -29,6 +29,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/OwningPtr.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/Support/Allocator.h" #include <vector> @@ -120,7 +121,7 @@ class Preprocessor : public llvm::RefCountedBase<Preprocessor> { /// Selectors - This table contains all the selectors in the program. Unlike /// IdentifierTable above, this table *isn't* populated by the preprocessor. - /// It is declared/instantiated here because it's role/lifetime is + /// It is declared/expanded here because it's role/lifetime is /// conceptually similar the IdentifierTable. In addition, the current control /// flow (in clang::ParseAST()), make it convenient to put here. /// FIXME: Make sure the lifetime of Identifiers/Selectors *isn't* tied to @@ -219,9 +220,9 @@ class Preprocessor : public llvm::RefCountedBase<Preprocessor> { /// previous macro value. llvm::DenseMap<IdentifierInfo*, std::vector<MacroInfo*> > PragmaPushMacroInfo; - /// \brief Instantiation source location for the last macro that expanded + /// \brief Expansion source location for the last macro that expanded /// to no tokens. - SourceLocation LastEmptyMacroInstantiationLoc; + SourceLocation LastEmptyMacroExpansionLoc; // Various statistics we track for performance analysis. unsigned NumDirectives, NumIncluded, NumDefined, NumUndefined, NumPragma; @@ -240,7 +241,15 @@ class Preprocessor : public llvm::RefCountedBase<Preprocessor> { unsigned NumCachedTokenLexers; TokenLexer *TokenLexerCache[TokenLexerCacheSize]; - /// \brief A record of the macro definitions and instantiations that + /// \brief Keeps macro expanded tokens for TokenLexers. + // + /// Works like a stack; a TokenLexer adds the macro expanded tokens that is + /// going to lex in the cache and when it finishes the tokens are removed + /// from the end of the cache. + llvm::SmallVector<Token, 16> MacroExpandedTokens; + std::vector<std::pair<TokenLexer *, size_t> > MacroExpandingLexersStack; + + /// \brief A record of the macro definitions and expansions that /// occurred during preprocessing. /// /// This is an optional side structure that can be enabled with @@ -371,10 +380,10 @@ public: macro_iterator macro_begin(bool IncludeExternalMacros = true) const; macro_iterator macro_end(bool IncludeExternalMacros = true) const; - /// \brief Instantiation source location for the last macro that expanded + /// \brief Expansion source location for the last macro that expanded /// to no tokens. - SourceLocation getLastEmptyMacroInstantiationLoc() const { - return LastEmptyMacroInstantiationLoc; + SourceLocation getLastEmptyMacroExpansionLoc() const { + return LastEmptyMacroExpansionLoc; } const std::string &getPredefines() const { return Predefines; } @@ -442,7 +451,7 @@ public: /// \brief Create a new preprocessing record, which will keep track of /// all macro expansions, macro definitions, etc. - void createPreprocessingRecord(bool IncludeNestedMacroInstantiations); + void createPreprocessingRecord(bool IncludeNestedMacroExpansions); /// EnterMainSourceFile - Enter the specified FileID as the main source file, /// which implicitly adds the builtin defines etc. @@ -658,7 +667,7 @@ public: /// getSpelling() - Return the 'spelling' of the token at the given /// location; does not go up to the spelling location or down to the - /// instantiation location. + /// expansion location. /// /// \param buffer A buffer which will be used only if the token requires /// "cleaning", e.g. if it contains trigraphs or escaped newlines @@ -721,7 +730,7 @@ public: /// CreateString - Plop the specified string into a scratch buffer and set the /// specified token's location and length to it. If specified, the source - /// location provides a location of the instantiation point of the token. + /// location provides a location of the expansion point of the token. void CreateString(const char *Buf, unsigned Len, Token &Tok, SourceLocation SourceLoc = SourceLocation()); @@ -744,6 +753,18 @@ public: return Lexer::getLocForEndOfToken(Loc, Offset, SourceMgr, Features); } + /// \brief Returns true if the given MacroID location points at the first + /// token of the macro expansion. + bool isAtStartOfMacroExpansion(SourceLocation loc) const { + return Lexer::isAtStartOfMacroExpansion(loc, SourceMgr, Features); + } + + /// \brief Returns true if the given MacroID location points at the last + /// token of the macro expansion. + bool isAtEndOfMacroExpansion(SourceLocation loc) const { + return Lexer::isAtEndOfMacroExpansion(loc, SourceMgr, Features); + } + /// DumpToken - Print the token to stderr, used for debugging. /// void DumpToken(const Token &Tok, bool DumpFlags = false) const; @@ -770,6 +791,8 @@ public: void PrintStats(); + size_t getTotalMemory() const; + /// HandleMicrosoftCommentPaste - When the macro expander pastes together a /// comment (/##/) in microsoft mode, this method handles updating the current /// state, returning the token on the next source line. @@ -977,6 +1000,16 @@ private: /// the macro should not be expanded return true, otherwise return false. bool HandleMacroExpandedIdentifier(Token &Tok, MacroInfo *MI); + /// \brief Cache macro expanded tokens for TokenLexers. + // + /// Works like a stack; a TokenLexer adds the macro expanded tokens that is + /// going to lex in the cache and when it finishes the tokens are removed + /// from the end of the cache. + Token *cacheMacroExpandedTokens(TokenLexer *tokLexer, + llvm::ArrayRef<Token> tokens); + void removeCachedMacroExpandedTokensOfLastLexer(); + friend void TokenLexer::ExpandFunctionArguments(); + /// isNextPPTokenLParen - Determine whether the next preprocessor token to be /// lexed is a '('. If so, consume the token and return true, if not, this /// method should have no observable side-effect on the lexed tokens. @@ -986,7 +1019,7 @@ private: /// invoked to read all of the formal arguments specified for the macro /// invocation. This returns null on error. MacroArgs *ReadFunctionLikeMacroArgs(Token &MacroName, MacroInfo *MI, - SourceLocation &InstantiationEnd); + SourceLocation &ExpansionEnd); /// ExpandBuiltinMacro - If an identifier token is read that is to be expanded /// as a builtin macro, handle it and return the next token as 'Tok'. diff --git a/include/clang/Lex/Token.h b/include/clang/Lex/Token.h index edcfcc10d2ee..9cf11d9a64c4 100644 --- a/include/clang/Lex/Token.h +++ b/include/clang/Lex/Token.h @@ -63,9 +63,7 @@ class Token { /// Kind - The actual flavor of token this is. /// - unsigned char Kind; // DON'T make Kind a 'tok::TokenKind'; - // MSVC will treat it as a signed char and - // TokenKinds > 127 won't be handled correctly. + unsigned short Kind; /// Flags - Bits we track about this token, members of the TokenFlags enum. unsigned char Flags; diff --git a/include/clang/Lex/TokenLexer.h b/include/clang/Lex/TokenLexer.h index 6ae00cd58658..45ff8a03442e 100644 --- a/include/clang/Lex/TokenLexer.h +++ b/include/clang/Lex/TokenLexer.h @@ -43,10 +43,13 @@ class TokenLexer { /// Tokens - This is the pointer to an array of tokens that the macro is /// defined to, with arguments expanded for function-like macros. If this is /// a token stream, these are the tokens we are returning. This points into - /// the macro definition we are lexing from, a scratch buffer allocated from - /// the preprocessor's bump pointer allocator, or some other buffer that we - /// may or may not own (depending on OwnsTokens). + /// the macro definition we are lexing from, a cache buffer that is owned by + /// the preprocessor, or some other buffer that we may or may not own + /// (depending on OwnsTokens). + /// Note that if it points into Preprocessor's cache buffer, the Preprocessor + /// may update the pointer as needed. const Token *Tokens; + friend class Preprocessor; /// NumTokens - This is the length of the Tokens array. /// @@ -56,9 +59,20 @@ class TokenLexer { /// unsigned CurToken; - /// InstantiateLocStart/End - The source location range where this macro was - /// instantiated. - SourceLocation InstantiateLocStart, InstantiateLocEnd; + /// ExpandLocStart/End - The source location range where this macro was + /// expanded. + SourceLocation ExpandLocStart, ExpandLocEnd; + + /// \brief Source location pointing at the source location entry chunk that + /// was reserved for the current macro expansion. + SourceLocation MacroExpansionStart; + + /// \brief The offset of the macro expansion in the + /// "source location address space". + unsigned MacroStartSLocOffset; + + /// \brief FileID/offset of the start of the macro definition. + std::pair<FileID, unsigned> MacroDefStartInfo; /// Lexical information about the expansion point of the macro: the identifier /// that the macro expanded from had these properties. @@ -148,9 +162,14 @@ private: /// HandleMicrosoftCommentPaste - In microsoft compatibility mode, /##/ pastes /// together to form a comment that comments out everything in the current /// macro, other active macros, and anything left on the current physical - /// source line of the instantiated buffer. Handle this by returning the + /// source line of the expanded buffer. Handle this by returning the /// first token on the next line. void HandleMicrosoftCommentPaste(Token &Tok); + + /// \brief If \arg loc is a FileID and points inside the current macro + /// definition, returns the appropriate source location pointing at the + /// macro expansion source location entry. + SourceLocation getMacroExpansionLocation(SourceLocation loc) const; }; } // end namespace clang diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index e4cdc27c436a..8f49ddad2cec 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_PARSE_PARSER_H #include "clang/Basic/Specifiers.h" +#include "clang/Basic/DelayedCleanupPool.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/CodeCompletionHandler.h" #include "clang/Sema/Sema.h" @@ -170,6 +171,10 @@ class Parser : public CodeCompletionHandler { /// Factory object for creating AttributeList objects. AttributeFactory AttrFactory; + /// \brief Gathers and cleans up objects when parsing of a top-level + /// declaration is finished. + DelayedCleanupPool TopLevelDeclCleanupPool; + public: Parser(Preprocessor &PP, Sema &Actions); ~Parser(); @@ -419,25 +424,7 @@ private: Tok.setAnnotationValue(ER.get()); } - /// TryAnnotateTypeOrScopeToken - If the current token position is on a - /// typename (possibly qualified in C++) or a C++ scope specifier not followed - /// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens - /// with a single annotation token representing the typename or C++ scope - /// respectively. - /// This simplifies handling of C++ scope specifiers and allows efficient - /// backtracking without the need to re-parse and resolve nested-names and - /// typenames. - /// It will mainly be called when we expect to treat identifiers as typenames - /// (if they are typenames). For example, in C we do not expect identifiers - /// inside expressions to be treated as typenames so it will not be called - /// for expressions in C. - /// - /// This returns true if the token was annotated. bool TryAnnotateTypeOrScopeToken(bool EnteringContext = false); - - /// TryAnnotateCXXScopeToken - Like TryAnnotateTypeOrScopeToken but - /// only annotates C++ scope specifiers. This returns true if there - /// was an unrecoverable error. bool TryAnnotateCXXScopeToken(bool EnteringContext = false); /// TryAltiVecToken - Check for context-sensitive AltiVec identifier tokens, @@ -467,7 +454,12 @@ private: bool TryAltiVecTokenOutOfLine(DeclSpec &DS, SourceLocation Loc, const char *&PrevSpec, unsigned &DiagID, bool &isInvalid); - + + /// \brief Get the TemplateIdAnnotation from the token and put it in the + /// cleanup pool so that it gets destroyed when parsing the current top level + /// declaration is finished. + TemplateIdAnnotation *takeTemplateIdAnnotation(const Token &tok); + /// TentativeParsingAction - An object that is used as a kind of "tentative /// parsing transaction". It gets instantiated to mark the token position and /// after the token consumption is done, Commit() or Revert() is called to @@ -507,12 +499,6 @@ private: }; - /// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'), - /// this helper function matches and consumes the specified RHS token if - /// present. If not present, it emits the specified diagnostic indicating - /// that the parser failed to match the RHS of the token at LHSLoc. LHSName - /// should be the name of the unmatched LHS token. This returns the location - /// of the consumed token. SourceLocation MatchRHSPunctuation(tok::TokenKind RHSTok, SourceLocation LHSLoc); @@ -1125,10 +1111,10 @@ private: ExprResult ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, bool &NotCastExpr, - ParsedType TypeOfCast); + bool isTypeCast); ExprResult ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand = false, - ParsedType TypeOfCast = ParsedType()); + bool isTypeCast = false); /// Returns true if the next token would start a postfix-expression /// suffix. @@ -1169,7 +1155,7 @@ private: }; ExprResult ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, - ParsedType TypeOfCast, + bool isTypeCast, ParsedType &CastTy, SourceLocation &RParenLoc); @@ -1328,6 +1314,9 @@ private: StmtResult ParseDefaultStatement(ParsedAttributes &Attr); StmtResult ParseCompoundStatement(ParsedAttributes &Attr, bool isStmtExpr = false); + StmtResult ParseCompoundStatement(ParsedAttributes &Attr, + bool isStmtExpr, + unsigned ScopeFlags); StmtResult ParseCompoundStatementBody(bool isStmtExpr = false); bool ParseParenExprOrCondition(ExprResult &ExprResult, Decl *&DeclResult, @@ -1375,6 +1364,7 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names, StmtResult ParseObjCTryStmt(SourceLocation atLoc); StmtResult ParseObjCThrowStmt(SourceLocation atLoc); StmtResult ParseObjCSynchronizedStmt(SourceLocation atLoc); + StmtResult ParseObjCAutoreleasePoolStmt(SourceLocation atLoc); //===--------------------------------------------------------------------===// @@ -1439,7 +1429,7 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names, const ParsedTemplateInfo &TemplateInfo = ParsedTemplateInfo(), bool SuppressDeclarations = false); - void ParseSpecifierQualifierList(DeclSpec &DS); + void ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS = AS_none); void ParseObjCTypeQualifierList(ObjCDeclSpec &DS, ObjCTypeNameContext Context); @@ -1616,7 +1606,10 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names, TypeResult ParseTypeName(SourceRange *Range = 0, Declarator::TheContext Context - = Declarator::TypeNameContext); + = Declarator::TypeNameContext, + ObjCDeclSpec *objcQuals = 0, + AccessSpecifier AS = AS_none, + Decl **OwnedType = 0); void ParseBlockId(); void ProhibitAttributes(ParsedAttributesWithRange &attrs) { @@ -1742,10 +1735,15 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names, void ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, ParsedAttributes &attrs, bool RequiresArg = false); - void ParseFunctionDeclaratorIdentifierList(SourceLocation LParenLoc, - IdentifierInfo *FirstIdent, - SourceLocation FirstIdentLoc, - Declarator &D); + bool isFunctionDeclaratorIdentifierList(); + void ParseFunctionDeclaratorIdentifierList( + Declarator &D, + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> &ParamInfo); + void ParseParameterDeclarationClause( + Declarator &D, + ParsedAttributes &attrs, + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> &ParamInfo, + SourceLocation &EllipsisLoc); void ParseBracketDeclarator(Declarator &D); //===--------------------------------------------------------------------===// @@ -1766,7 +1764,8 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names, Decl *ParseUsingDirectiveOrDeclaration(unsigned Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd, - ParsedAttributesWithRange &attrs); + ParsedAttributesWithRange &attrs, + Decl **OwnedType = 0); Decl *ParseUsingDirective(unsigned Context, SourceLocation UsingLoc, SourceLocation &DeclEnd, @@ -1775,7 +1774,8 @@ bool ParseAsmOperandsOpt(llvm::SmallVectorImpl<IdentifierInfo *> &Names, const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, - AccessSpecifier AS = AS_none); + AccessSpecifier AS = AS_none, + Decl **OwnedType = 0); Decl *ParseStaticAssertDeclaration(SourceLocation &DeclEnd); Decl *ParseNamespaceAlias(SourceLocation NamespaceLoc, SourceLocation AliasLoc, IdentifierInfo *Alias, diff --git a/include/clang/Rewrite/FixItRewriter.h b/include/clang/Rewrite/FixItRewriter.h index 26a0d72baa2a..bab99624ba4a 100644 --- a/include/clang/Rewrite/FixItRewriter.h +++ b/include/clang/Rewrite/FixItRewriter.h @@ -18,7 +18,6 @@ #include "clang/Basic/Diagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Rewrite/Rewriter.h" -#include "llvm/ADT/SmallVector.h" namespace llvm { class raw_ostream; } diff --git a/include/clang/Rewrite/Rewriter.h b/include/clang/Rewrite/Rewriter.h index 7861e9992891..676744ada36c 100644 --- a/include/clang/Rewrite/Rewriter.h +++ b/include/clang/Rewrite/Rewriter.h @@ -183,8 +183,11 @@ public: /// InsertText - Insert the specified string at the specified location in the /// original buffer. This method returns true (and does nothing) if the input /// location was not rewritable, false otherwise. + /// + /// \param indentNewLines if true new lines in the string are indented + /// using the indentation of the source line in position \arg Loc. bool InsertText(SourceLocation Loc, llvm::StringRef Str, - bool InsertAfter = true); + bool InsertAfter = true, bool indentNewLines = false); /// InsertTextAfter - Insert the specified string at the specified location in /// the original buffer. This method returns true (and does nothing) if diff --git a/include/clang/Sema/AnalysisBasedWarnings.h b/include/clang/Sema/AnalysisBasedWarnings.h index 8b389b169b3c..8e781cd521c2 100644 --- a/include/clang/Sema/AnalysisBasedWarnings.h +++ b/include/clang/Sema/AnalysisBasedWarnings.h @@ -14,7 +14,6 @@ #ifndef LLVM_CLANG_SEMA_ANALYSIS_WARNINGS_H #define LLVM_CLANG_SEMA_ANALYSIS_WARNINGS_H -#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/DenseMap.h" namespace clang { @@ -50,6 +49,41 @@ private: enum VisitFlag { NotVisited = 0, Visited = 1, Pending = 2 }; llvm::DenseMap<const FunctionDecl*, VisitFlag> VisitedFD; + /// \name Statistics + /// @{ + + /// \brief Number of function CFGs built and analyzed. + unsigned NumFunctionsAnalyzed; + + /// \brief Number of functions for which the CFG could not be successfully + /// built. + unsigned NumFunctionsWithBadCFGs; + + /// \brief Total number of blocks across all CFGs. + unsigned NumCFGBlocks; + + /// \brief Largest number of CFG blocks for a single function analyzed. + unsigned MaxCFGBlocksPerFunction; + + /// \brief Total number of CFGs with variables analyzed for uninitialized + /// uses. + unsigned NumUninitAnalysisFunctions; + + /// \brief Total number of variables analyzed for uninitialized uses. + unsigned NumUninitAnalysisVariables; + + /// \brief Max number of variables analyzed for uninitialized uses in a single + /// function. + unsigned MaxUninitAnalysisVariablesPerFunction; + + /// \brief Total number of block visits during uninitialized use analysis. + unsigned NumUninitAnalysisBlockVisits; + + /// \brief Max number of block visits during uninitialized use analysis of + /// a single function. + unsigned MaxUninitAnalysisBlockVisitsPerFunction; + + /// @} public: AnalysisBasedWarnings(Sema &s); @@ -58,6 +92,8 @@ public: const Decl *D, const BlockExpr *blkExpr); Policy getDefaultPolicy() { return DefaultPolicy; } + + void PrintStats() const; }; }} // end namespace clang::sema diff --git a/include/clang/Sema/AttributeList.h b/include/clang/Sema/AttributeList.h index 72cd47589f91..5d2d6c2ec691 100644 --- a/include/clang/Sema/AttributeList.h +++ b/include/clang/Sema/AttributeList.h @@ -77,6 +77,8 @@ private: /// availability attribute. unsigned IsAvailability : 1; + unsigned AttrKind : 8; + /// \brief The location of the 'unavailable' keyword in an /// availability attribute. SourceLocation UnavailableLoc; @@ -123,6 +125,7 @@ private: DeclspecAttribute(declspec), CXX0XAttribute(cxx0x), Invalid(false), IsAvailability(false), NextInPosition(0), NextInPool(0) { if (numArgs) memcpy(getArgsBuffer(), args, numArgs * sizeof(Expr*)); + AttrKind = getKind(getName()); } AttributeList(IdentifierInfo *attrName, SourceLocation attrLoc, @@ -141,6 +144,7 @@ private: new (&getAvailabilitySlot(IntroducedSlot)) AvailabilityChange(introduced); new (&getAvailabilitySlot(DeprecatedSlot)) AvailabilityChange(deprecated); new (&getAvailabilitySlot(ObsoletedSlot)) AvailabilityChange(obsoleted); + AttrKind = getKind(getName()); } friend class AttributePool; @@ -148,20 +152,22 @@ private: public: enum Kind { // Please keep this list alphabetized. - AT_IBAction, // Clang-specific. - AT_IBOutlet, // Clang-specific. - AT_IBOutletCollection, // Clang-specific. AT_address_space, AT_alias, AT_aligned, AT_always_inline, AT_analyzer_noreturn, AT_annotate, + AT_arc_weakref_unavailable, AT_availability, // Clang-specific AT_base_check, AT_blocks, AT_carries_dependency, AT_cdecl, + AT_cf_consumed, // Clang-specific. + AT_cf_returns_autoreleased, // Clang-specific. + AT_cf_returns_not_retained, // Clang-specific. + AT_cf_returns_retained, // Clang-specific. AT_cleanup, AT_common, AT_const, @@ -179,32 +185,36 @@ public: AT_global, AT_gnu_inline, AT_host, + AT_IBAction, // Clang-specific. + AT_IBOutlet, // Clang-specific. + AT_IBOutletCollection, // Clang-specific. + AT_init_priority, AT_launch_bounds, AT_malloc, AT_may_alias, AT_mode, + AT_MsStruct, + AT_naked, AT_neon_polyvector_type, // Clang-specific. AT_neon_vector_type, // Clang-specific. - AT_naked, - AT_nodebug, - AT_noinline, AT_no_instrument_function, AT_nocommon, + AT_nodebug, + AT_noinline, AT_nonnull, AT_noreturn, AT_nothrow, - AT_nsobject, - AT_objc_exception, - AT_objc_method_family, - AT_cf_returns_not_retained, // Clang-specific. - AT_cf_returns_retained, // Clang-specific. - AT_ns_returns_not_retained, // Clang-specific. - AT_ns_returns_retained, // Clang-specific. - AT_ns_returns_autoreleased, // Clang-specific. - AT_cf_consumed, // Clang-specific. AT_ns_consumed, // Clang-specific. AT_ns_consumes_self, // Clang-specific. + AT_ns_returns_autoreleased, // Clang-specific. + AT_ns_returns_not_retained, // Clang-specific. + AT_ns_returns_retained, // Clang-specific. + AT_nsobject, + AT_objc_exception, AT_objc_gc, + AT_objc_method_family, + AT_objc_ownership, // Clang-specific. + AT_objc_precise_lifetime, // Clang-specific. AT_opencl_image_access, // OpenCL-specific. AT_opencl_kernel_function, // OpenCL-specific. AT_overloadable, // Clang-specific. @@ -216,6 +226,7 @@ public: AT_pcs, // ARM specific AT_pure, AT_regparm, + AT_reqd_wg_size, AT_section, AT_sentinel, AT_shared, @@ -231,11 +242,8 @@ public: AT_visibility, AT_warn_unused_result, AT_weak, - AT_weakref, AT_weak_import, - AT_reqd_wg_size, - AT_init_priority, - AT_MsStruct, + AT_weakref, IgnoredAttribute, UnknownAttribute }; @@ -256,7 +264,7 @@ public: bool isInvalid() const { return Invalid; } void setInvalid(bool b = true) const { Invalid = b; } - Kind getKind() const { return getKind(getName()); } + Kind getKind() const { return Kind(AttrKind); } static Kind getKind(const IdentifierInfo *Name); AttributeList *getNext() const { return NextInPosition; } diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index d3221809b981..74b0105b3310 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -185,12 +185,26 @@ public: /// is expected. CCC_ObjCMessageReceiver, /// \brief Code completion occurred on the right-hand side of a member - /// access expression. + /// access expression using the dot operator. /// /// The results of this completion are the members of the type being /// accessed. The type itself is available via /// \c CodeCompletionContext::getType(). - CCC_MemberAccess, + CCC_DotMemberAccess, + /// \brief Code completion occurred on the right-hand side of a member + /// access expression using the arrow operator. + /// + /// The results of this completion are the members of the type being + /// accessed. The type itself is available via + /// \c CodeCompletionContext::getType(). + CCC_ArrowMemberAccess, + /// \brief Code completion occurred on the right-hand side of an Objective-C + /// property access expression. + /// + /// The results of this completion are the members of the type being + /// accessed. The type itself is available via + /// \c CodeCompletionContext::getType(). + CCC_ObjCPropertyAccess, /// \brief Code completion occurred after the "enum" keyword, to indicate /// an enumeration name. CCC_EnumTag, @@ -235,6 +249,15 @@ public: /// \brief Code completion in a parenthesized expression, which means that /// we may also have types here in C and Objective-C (as well as in C++). CCC_ParenthesizedExpression, + /// \brief Code completion where an Objective-C instance message is expcted. + CCC_ObjCInstanceMessage, + /// \brief Code completion where an Objective-C class message is expected. + CCC_ObjCClassMessage, + /// \brief Code completion where a superclass of an Objective-C class is + /// expected. + CCC_ObjCSuperclass, + /// \brief Code completion where an Objective-C category name is expected. + CCC_ObjCCategoryName, /// \brief An unknown context, in which we are recovering from a parsing /// error and don't know which completions we should give. CCC_Recovery @@ -256,7 +279,8 @@ public: /// \brief Construct a new code-completion context of the given kind. CodeCompletionContext(enum Kind Kind, QualType T) : Kind(Kind) { - if (Kind == CCC_MemberAccess) + if (Kind == CCC_DotMemberAccess || Kind == CCC_ArrowMemberAccess || + Kind == CCC_ObjCPropertyAccess) BaseType = T; else PreferredType = T; diff --git a/include/clang/Sema/DeclSpec.h b/include/clang/Sema/DeclSpec.h index 7ce4e009432c..a66649955c8c 100644 --- a/include/clang/Sema/DeclSpec.h +++ b/include/clang/Sema/DeclSpec.h @@ -40,6 +40,7 @@ namespace clang { class NamespaceDecl; class NestedNameSpecifier; class NestedNameSpecifierLoc; + class ObjCDeclSpec; class Preprocessor; class Declarator; struct TemplateIdAnnotation; @@ -153,6 +154,12 @@ public: /// copied. NestedNameSpecifierLoc getWithLocInContext(ASTContext &Context) const; + /// \brief Retrieve the location of the name in the last qualifier + /// in this nested name specifier. For example: + /// ::foo::bar<0>:: + /// ^~~ + SourceLocation getLastQualifierNameLoc() const; + /// No scope specifier. bool isEmpty() const { return !Range.isValid(); } /// A scope specifier is present, but may be valid or invalid. @@ -344,6 +351,8 @@ private: void SaveWrittenBuiltinSpecs(); void SaveStorageSpecifierAsWritten(); + ObjCDeclSpec *ObjCQualifiers; + static bool isTypeRep(TST T) { return (T == TST_typename || T == TST_typeofType || T == TST_underlyingType); @@ -383,7 +392,8 @@ public: ProtocolQualifiers(0), NumProtocolQualifiers(0), ProtocolLocs(0), - writtenBS() { + writtenBS(), + ObjCQualifiers(0) { } ~DeclSpec() { delete [] ProtocolQualifiers; @@ -653,6 +663,9 @@ public: return writtenBS; } + ObjCDeclSpec *getObjCQualifiers() const { return ObjCQualifiers; } + void setObjCQualifiers(ObjCDeclSpec *quals) { ObjCQualifiers = quals; } + /// isMissingDeclaratorOk - This checks if this DeclSpec can stand alone, /// without a Declarator. Only tag declspecs can stand alone. bool isMissingDeclaratorOk(); @@ -689,7 +702,10 @@ public: DQ_PR_copy = 0x20, DQ_PR_nonatomic = 0x40, DQ_PR_setter = 0x80, - DQ_PR_atomic = 0x100 + DQ_PR_atomic = 0x100, + DQ_PR_weak = 0x200, + DQ_PR_strong = 0x400, + DQ_PR_unsafe_unretained = 0x800 }; @@ -723,7 +739,7 @@ private: ObjCDeclQualifier objcDeclQualifier : 6; // NOTE: VC++ treats enums as signed, avoid using ObjCPropertyAttributeKind - unsigned PropertyAttributes : 9; + unsigned PropertyAttributes : 12; IdentifierInfo *GetterName; // getter name of NULL if no getter IdentifierInfo *SetterName; // setter name of NULL if no setter }; @@ -751,7 +767,9 @@ public: /// \brief A destructor name. IK_DestructorName, /// \brief A template-id, e.g., f<int>. - IK_TemplateId + IK_TemplateId, + /// \brief An implicit 'self' parameter + IK_ImplicitSelfParam } Kind; /// \brief Anonymous union that holds extra data associated with the @@ -804,7 +822,7 @@ public: SourceLocation EndLocation; UnqualifiedId() : Kind(IK_Identifier), Identifier(0) { } - + /// \brief Do not use this copy constructor. It is temporary, and only /// exists because we are holding FieldDeclarators in a SmallVector when we /// don't actually need them. @@ -831,6 +849,7 @@ public: /// \brief Determine what kind of name we have. IdKind getKind() const { return Kind; } + void setKind(IdKind kind) { Kind = kind; } /// \brief Specify that this unqualified-id was parsed as an identifier. /// @@ -1078,6 +1097,10 @@ struct DeclaratorChunk { /// If this is an invalid location, there is no ref-qualifier. unsigned RefQualifierLoc; + /// \brief The location of the 'mutable' qualifer in a lambda-declarator, if + /// any. + unsigned MutableLoc; + /// \brief When ExceptionSpecType isn't EST_None or EST_Delayed, the /// location of the keyword introducing the spec. unsigned ExceptionSpecLoc; @@ -1139,10 +1162,19 @@ struct DeclaratorChunk { return SourceLocation::getFromRawEncoding(RefQualifierLoc); } + /// \brief Retrieve the location of the 'mutable' qualifier, if any. + SourceLocation getMutableLoc() const { + return SourceLocation::getFromRawEncoding(MutableLoc); + } + /// \brief Determine whether this function declaration contains a /// ref-qualifier. bool hasRefQualifier() const { return getRefQualifierLoc().isValid(); } + /// \brief Determine whether this lambda-declarator contains a 'mutable' + /// qualifier. + bool hasMutableQualifier() const { return getMutableLoc().isValid(); } + /// \brief Get the type of exception specification this function has. ExceptionSpecificationType getExceptionSpecType() const { return static_cast<ExceptionSpecificationType>(ExceptionSpecType); @@ -1266,6 +1298,7 @@ struct DeclaratorChunk { unsigned TypeQuals, bool RefQualifierIsLvalueRef, SourceLocation RefQualifierLoc, + SourceLocation MutableLoc, ExceptionSpecificationType ESpecType, SourceLocation ESpecLoc, ParsedType *Exceptions, @@ -1339,7 +1372,9 @@ public: ForContext, // Declaration within first part of a for loop. ConditionContext, // Condition declaration in a C++ if/switch/while/for. TemplateParamContext,// Within a template parameter list. + CXXNewContext, // C++ new-expression. CXXCatchContext, // C++ catch exception-declaration + ObjCCatchContext, // Objective-C catch exception-declaration BlockLiteralContext, // Block literal declarator. TemplateTypeArgContext, // Template type argument. AliasDeclContext, // C++0x alias-declaration. @@ -1489,7 +1524,9 @@ public: case PrototypeContext: case ObjCPrototypeContext: case TemplateParamContext: + case CXXNewContext: case CXXCatchContext: + case ObjCCatchContext: case BlockLiteralContext: case TemplateTypeArgContext: return true; @@ -1511,9 +1548,11 @@ public: case PrototypeContext: case TemplateParamContext: case CXXCatchContext: + case ObjCCatchContext: return true; case TypeNameContext: + case CXXNewContext: case AliasDeclContext: case AliasTemplateContext: case ObjCPrototypeContext: @@ -1542,7 +1581,9 @@ public: case ObjCPrototypeContext: case TemplateParamContext: case CXXCatchContext: + case ObjCCatchContext: case TypeNameContext: + case CXXNewContext: case AliasDeclContext: case AliasTemplateContext: case BlockLiteralContext: @@ -1687,6 +1728,14 @@ public: return const_cast<Declarator*>(this)->getFunctionTypeInfo(); } + /// \brief Determine whether the declaration that will be produced from + /// this declaration will be a function. + /// + /// A declaration can declare a function even if the declarator itself + /// isn't a function declarator, if the type specifier refers to a function + /// type. This routine checks for both cases. + bool isDeclarationOfFunction() const; + /// takeAttributes - Takes attributes from the given parsed-attributes /// set and add them to this declarator. /// diff --git a/include/clang/Sema/DelayedDiagnostic.h b/include/clang/Sema/DelayedDiagnostic.h index 8395138ab612..8ab938226482 100644 --- a/include/clang/Sema/DelayedDiagnostic.h +++ b/include/clang/Sema/DelayedDiagnostic.h @@ -112,7 +112,7 @@ private: /// the complete parsing of the current declaration. class DelayedDiagnostic { public: - enum DDKind { Deprecation, Access }; + enum DDKind { Deprecation, Access, ForbiddenType }; unsigned char Kind; // actually a DDKind bool Triggered; @@ -135,6 +135,20 @@ public: return DD; } + static DelayedDiagnostic makeForbiddenType(SourceLocation loc, + unsigned diagnostic, + QualType type, + unsigned argument) { + DelayedDiagnostic DD; + DD.Kind = ForbiddenType; + DD.Triggered = false; + DD.Loc = loc; + DD.ForbiddenTypeData.Diagnostic = diagnostic; + DD.ForbiddenTypeData.OperandType = type.getAsOpaquePtr(); + DD.ForbiddenTypeData.Argument = argument; + return DD; + } + AccessedEntity &getAccessData() { assert(Kind == Access && "Not an access diagnostic."); return *reinterpret_cast<AccessedEntity*>(AccessData); @@ -155,6 +169,25 @@ public: DeprecationData.MessageLen); } + /// The diagnostic ID to emit. Used like so: + /// Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) + /// << diag.getForbiddenTypeOperand() + /// << diag.getForbiddenTypeArgument(); + unsigned getForbiddenTypeDiagnostic() const { + assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); + return ForbiddenTypeData.Diagnostic; + } + + unsigned getForbiddenTypeArgument() const { + assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); + return ForbiddenTypeData.Argument; + } + + QualType getForbiddenTypeOperand() const { + assert(Kind == ForbiddenType && "not a forbidden-type diagnostic"); + return QualType::getFromOpaquePtr(ForbiddenTypeData.OperandType); + } + private: union { /// Deprecation. @@ -164,6 +197,12 @@ private: size_t MessageLen; } DeprecationData; + struct { + unsigned Diagnostic; + unsigned Argument; + void *OperandType; + } ForbiddenTypeData; + /// Access control. char AccessData[sizeof(AccessedEntity)]; }; diff --git a/include/clang/Sema/ExternalSemaSource.h b/include/clang/Sema/ExternalSemaSource.h index e2b083e83047..072e1b58d138 100644 --- a/include/clang/Sema/ExternalSemaSource.h +++ b/include/clang/Sema/ExternalSemaSource.h @@ -49,6 +49,11 @@ public: /// instance and factory methods, respectively, with this selector. virtual std::pair<ObjCMethodList,ObjCMethodList> ReadMethodPool(Selector Sel); + /// \brief Load the set of namespaces that are known to the external source, + /// which will be used during typo correction. + virtual void ReadKnownNamespaces( + llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces); + /// \brief Do last resort, unqualified lookup on a LookupResult that /// Sema cannot find. /// diff --git a/include/clang/Sema/Initialization.h b/include/clang/Sema/Initialization.h index 5dc4438671bb..df6138c7f039 100644 --- a/include/clang/Sema/Initialization.h +++ b/include/clang/Sema/Initialization.h @@ -86,9 +86,13 @@ private: QualType Type; union { - /// \brief When Kind == EK_Variable, EK_Parameter, or EK_Member, - /// the VarDecl, ParmVarDecl, or FieldDecl, respectively. + /// \brief When Kind == EK_Variable or EK_Member, the VarDecl or + /// FieldDecl, respectively. DeclaratorDecl *VariableOrMember; + + /// \brief When Kind == EK_Parameter, the ParmVarDecl, with the + /// low bit indicating whether the parameter is "consumed". + uintptr_t Parameter; /// \brief When Kind == EK_Temporary, the type source information for /// the temporary. @@ -123,11 +127,6 @@ private: : Kind(EK_Variable), Parent(0), Type(Var->getType()), VariableOrMember(Var) { } - /// \brief Create the initialization entity for a parameter. - InitializedEntity(ParmVarDecl *Parm) - : Kind(EK_Parameter), Parent(0), Type(Parm->getType().getUnqualifiedType()), - VariableOrMember(Parm) { } - /// \brief Create the initialization entity for the result of a /// function, throwing an object, performing an explicit cast, or /// initializing a parameter for which there is no declaration. @@ -157,20 +156,29 @@ public: /// \brief Create the initialization entity for a parameter. static InitializedEntity InitializeParameter(ASTContext &Context, ParmVarDecl *Parm) { - InitializedEntity Res(Parm); - Res.Type = Context.getVariableArrayDecayedType(Res.Type); - return Res; + bool Consumed = (Context.getLangOptions().ObjCAutoRefCount && + Parm->hasAttr<NSConsumedAttr>()); + + InitializedEntity Entity; + Entity.Kind = EK_Parameter; + Entity.Type = Context.getVariableArrayDecayedType( + Parm->getType().getUnqualifiedType()); + Entity.Parent = 0; + Entity.Parameter + = (static_cast<uintptr_t>(Consumed) | reinterpret_cast<uintptr_t>(Parm)); + return Entity; } /// \brief Create the initialization entity for a parameter that is /// only known by its type. static InitializedEntity InitializeParameter(ASTContext &Context, - QualType Type) { + QualType Type, + bool Consumed) { InitializedEntity Entity; Entity.Kind = EK_Parameter; Entity.Type = Context.getVariableArrayDecayedType(Type); Entity.Parent = 0; - Entity.VariableOrMember = 0; + Entity.Parameter = (Consumed); return Entity; } @@ -268,6 +276,13 @@ public: /// \brief Determine whether this initialization allows the named return /// value optimization, which also applies to thrown objects. bool allowsNRVO() const; + + /// \brief Determine whether this initialization consumes the + /// parameter. + bool isParameterConsumed() const { + assert(getKind() == EK_Parameter && "Not a parameter"); + return (Parameter & 1); + } /// \brief Retrieve the base specifier. CXXBaseSpecifier *getBaseSpecifier() const { @@ -287,7 +302,7 @@ public: assert(getKind() == EK_Result && "No 'return' location!"); return SourceLocation::getFromRawEncoding(LocAndNRVO.Location); } - + /// \brief Determine the location of the 'throw' keyword when initializing /// an exception object. SourceLocation getThrowLoc() const { @@ -325,8 +340,10 @@ private: SIK_Value = IK_Value, ///< Value initialization SIK_ImplicitValue, ///< Implicit value initialization SIK_DirectCast, ///< Direct initialization due to a cast - /// \brief Direct initialization due to a C-style or functional cast. - SIK_DirectCStyleOrFunctionalCast + /// \brief Direct initialization due to a C-style cast. + SIK_DirectCStyleCast, + /// \brief Direct initialization due to a functional-style cast. + SIK_DirectFunctionalCast }; /// \brief The kind of initialization being performed. @@ -352,15 +369,29 @@ public: return InitializationKind(SIK_Direct, InitLoc, LParenLoc, RParenLoc); } - /// \brief Create a direct initialization due to a cast. - static InitializationKind CreateCast(SourceRange TypeRange, - bool IsCStyleCast) { - return InitializationKind(IsCStyleCast? SIK_DirectCStyleOrFunctionalCast - : SIK_DirectCast, + /// \brief Create a direct initialization due to a cast that isn't a C-style + /// or functional cast. + static InitializationKind CreateCast(SourceRange TypeRange) { + return InitializationKind(SIK_DirectCast, TypeRange.getBegin(), TypeRange.getBegin(), TypeRange.getEnd()); } + /// \brief Create a direct initialization for a C-style cast. + static InitializationKind CreateCStyleCast(SourceLocation StartLoc, + SourceRange TypeRange) { + return InitializationKind(SIK_DirectCStyleCast, + StartLoc, TypeRange.getBegin(), + TypeRange.getEnd()); + } + + /// \brief Create a direct initialization for a functional cast. + static InitializationKind CreateFunctionalCast(SourceRange TypeRange) { + return InitializationKind(SIK_DirectFunctionalCast, + TypeRange.getBegin(), TypeRange.getBegin(), + TypeRange.getEnd()); + } + /// \brief Create a copy initialization. static InitializationKind CreateCopy(SourceLocation InitLoc, SourceLocation EqualLoc) { @@ -393,12 +424,24 @@ public: /// \brief Determine whether this initialization is an explicit cast. bool isExplicitCast() const { - return Kind == SIK_DirectCast || Kind == SIK_DirectCStyleOrFunctionalCast; + return Kind == SIK_DirectCast || + Kind == SIK_DirectCStyleCast || + Kind == SIK_DirectFunctionalCast; } /// \brief Determine whether this initialization is a C-style cast. bool isCStyleOrFunctionalCast() const { - return Kind == SIK_DirectCStyleOrFunctionalCast; + return Kind == SIK_DirectCStyleCast || Kind == SIK_DirectFunctionalCast; + } + + /// brief Determine whether this is a C-style cast. + bool isCStyleCast() const { + return Kind == SIK_DirectCStyleCast; + } + + /// brief Determine whether this is a functional-style cast. + bool isFunctionalCast() const { + return Kind == SIK_DirectFunctionalCast; } /// \brief Determine whether this initialization is an implicit @@ -448,10 +491,7 @@ public: DependentSequence, /// \brief A normal sequence. - NormalSequence, - - /// \brief A reference binding. - ReferenceBinding // FIXME: Still looks redundant, but complicated. + NormalSequence }; /// \brief Describes the kind of a particular step in an initialization @@ -500,7 +540,13 @@ public: SK_ObjCObjectConversion, /// \brief Array initialization (from an array rvalue). /// This is a GNU C extension. - SK_ArrayInit + SK_ArrayInit, + /// \brief Pass an object by indirect copy-and-restore. + SK_PassByIndirectCopyRestore, + /// \brief Pass an object by indirect restore. + SK_PassByIndirectRestore, + /// \brief Produce an Objective-C object pointer. + SK_ProduceObjCObject }; /// \brief A single step in the initialization sequence. @@ -774,6 +820,13 @@ public: /// \brief Add an array initialization step. void AddArrayInitStep(QualType T); + /// \brief Add a step to pass an object by indirect copy-restore. + void AddPassByIndirectCopyRestoreStep(QualType T, bool shouldCopy); + + /// \brief Add a step to "produce" an Objective-C object (by + /// retaining it). + void AddProduceObjCObjectStep(QualType T); + /// \brief Note that this initialization sequence failed. void SetFailed(FailureKind Failure) { SequenceKind = FailedSequence; diff --git a/include/clang/Sema/LocInfoType.h b/include/clang/Sema/LocInfoType.h new file mode 100644 index 000000000000..e1d3ae905170 --- /dev/null +++ b/include/clang/Sema/LocInfoType.h @@ -0,0 +1,63 @@ +//===--- LocInfoType.h - Parsed Type with Location Information---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the LocInfoType class, which holds a type and its +// source-location information. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_SEMA_LOCINFOTYPE_H +#define LLVM_CLANG_SEMA_LOCINFOTYPE_H + +#include "clang/AST/Type.h" + +namespace clang { + +class TypeSourceInfo; + +/// \brief Holds a QualType and a TypeSourceInfo* that came out of a declarator +/// parsing. +/// +/// LocInfoType is a "transient" type, only needed for passing to/from Parser +/// and Sema, when we want to preserve type source info for a parsed type. +/// It will not participate in the type system semantics in any way. +class LocInfoType : public Type { + enum { + // The last number that can fit in Type's TC. + // Avoids conflict with an existing Type class. + LocInfo = Type::TypeLast + 1 + }; + + TypeSourceInfo *DeclInfo; + + LocInfoType(QualType ty, TypeSourceInfo *TInfo) + : Type((TypeClass)LocInfo, ty, ty->isDependentType(), + ty->isInstantiationDependentType(), + ty->isVariablyModifiedType(), + ty->containsUnexpandedParameterPack()), + DeclInfo(TInfo) { + assert(getTypeClass() == (TypeClass)LocInfo && "LocInfo didn't fit in TC?"); + } + friend class Sema; + + public: + QualType getType() const { return getCanonicalTypeInternal(); } + TypeSourceInfo *getTypeSourceInfo() const { return DeclInfo; } + + void getAsStringInternal(std::string &Str, + const PrintingPolicy &Policy) const; + + static bool classof(const Type *T) { + return T->getTypeClass() == (TypeClass)LocInfo; + } + static bool classof(const LocInfoType *) { return true; } +}; + +} // end namespace clang + +#endif // LLVM_CLANG_SEMA_LOCINFOTYPE_H diff --git a/include/clang/Sema/Overload.h b/include/clang/Sema/Overload.h index 55931f2318f6..32d4cbdac15e 100644 --- a/include/clang/Sema/Overload.h +++ b/include/clang/Sema/Overload.h @@ -77,6 +77,7 @@ namespace clang { ICK_Complex_Real, ///< Complex-real conversions (C99 6.3.1.7) ICK_Block_Pointer_Conversion, ///< Block Pointer conversions ICK_TransparentUnionConversion, /// Transparent Union Conversions + ICK_Writeback_Conversion, ///< Objective-C ARC writeback conversion ICK_Num_Conversion_Kinds ///< The number of conversion kinds }; @@ -100,10 +101,11 @@ namespace clang { /// 13.3.3.1.1) and are listed such that better conversion ranks /// have smaller values. enum ImplicitConversionRank { - ICR_Exact_Match = 0, ///< Exact Match - ICR_Promotion, ///< Promotion - ICR_Conversion, ///< Conversion - ICR_Complex_Real_Conversion ///< Complex <-> Real conversion + ICR_Exact_Match = 0, ///< Exact Match + ICR_Promotion, ///< Promotion + ICR_Conversion, ///< Conversion + ICR_Complex_Real_Conversion, ///< Complex <-> Real conversion + ICR_Writeback_Conversion ///< ObjC ARC writeback conversion }; ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind); @@ -137,6 +139,10 @@ namespace clang { /// (C++ 4.2p2). unsigned DeprecatedStringLiteralToCharPtr : 1; + /// \brief Whether the qualification conversion involves a change in the + /// Objective-C lifetime (for automatic reference counting). + unsigned QualificationIncludesObjCLifetime : 1; + /// IncompatibleObjC - Whether this is an Objective-C conversion /// that we should warn about (if we actually use it). unsigned IncompatibleObjC : 1; @@ -163,6 +169,10 @@ namespace clang { /// non-static member function without a ref-qualifier. unsigned BindsImplicitObjectArgumentWithoutRefQualifier : 1; + /// \brief Whether this binds a reference to an object with a different + /// Objective-C lifetime qualifier. + unsigned ObjCLifetimeConversionBinding : 1; + /// FromType - The type that this conversion is converting /// from. This is an opaque pointer that can be translated into a /// QualType. diff --git a/include/clang/Sema/Scope.h b/include/clang/Sema/Scope.h index 7514633c0ef8..95d29781e704 100644 --- a/include/clang/Sema/Scope.h +++ b/include/clang/Sema/Scope.h @@ -84,7 +84,10 @@ public: /// ThisScope - This is the scope of a struct/union/class definition, /// outside of any member function definition, where 'this' is nonetheless /// usable. - ThisScope = 0x1000 + ThisScope = 0x1000, + + /// TryScope - This is the scope of a C++ try statement. + TryScope = 0x2000 }; private: /// The parent scope for this scope. This is null for the translation-unit @@ -303,6 +306,9 @@ public: } return false; } + + /// \brief Determine whether this scope is a C++ 'try' block. + bool isTryScope() const { return getFlags() & Scope::TryScope; } typedef UsingDirectivesTy::iterator udir_iterator; typedef UsingDirectivesTy::const_iterator const_udir_iterator; diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6dd0d3ceab4c..263c1bd49ac9 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -20,6 +20,8 @@ #include "clang/Sema/IdentifierResolver.h" #include "clang/Sema/ObjCMethodList.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/LocInfoType.h" +#include "clang/Sema/TypoCorrection.h" #include "clang/AST/Expr.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExternalASTSource.h" @@ -45,6 +47,8 @@ namespace clang { class ASTConsumer; class ASTContext; class ASTMutationListener; + class ASTReader; + class ASTWriter; class ArrayType; class AttributeList; class BlockDecl; @@ -108,6 +112,7 @@ namespace clang { class ObjCInterfaceDecl; class ObjCIvarDecl; template <class T> class ObjCList; + class ObjCMessageExpr; class ObjCMethodDecl; class ObjCPropertyDecl; class ObjCProtocolDecl; @@ -156,43 +161,6 @@ namespace sema { class TemplateDeductionInfo; } -/// \brief Holds a QualType and a TypeSourceInfo* that came out of a declarator -/// parsing. -/// -/// LocInfoType is a "transient" type, only needed for passing to/from Parser -/// and Sema, when we want to preserve type source info for a parsed type. -/// It will not participate in the type system semantics in any way. -class LocInfoType : public Type { - enum { - // The last number that can fit in Type's TC. - // Avoids conflict with an existing Type class. - LocInfo = Type::TypeLast + 1 - }; - - TypeSourceInfo *DeclInfo; - - LocInfoType(QualType ty, TypeSourceInfo *TInfo) - : Type((TypeClass)LocInfo, ty, ty->isDependentType(), - ty->isVariablyModifiedType(), - ty->containsUnexpandedParameterPack()), - DeclInfo(TInfo) { - assert(getTypeClass() == (TypeClass)LocInfo && "LocInfo didn't fit in TC?"); - } - friend class Sema; - -public: - QualType getType() const { return getCanonicalTypeInternal(); } - TypeSourceInfo *getTypeSourceInfo() const { return DeclInfo; } - - void getAsStringInternal(std::string &Str, - const PrintingPolicy &Policy) const; - - static bool classof(const Type *T) { - return T->getTypeClass() == (TypeClass)LocInfo; - } - static bool classof(const LocInfoType *) { return true; } -}; - // FIXME: No way to easily map from TemplateTypeParmTypes to // TemplateTypeParmDecls, so we have this horrible PointerUnion. typedef std::pair<llvm::PointerUnion<const TemplateTypeParmType*, NamedDecl*>, @@ -225,6 +193,9 @@ public: Diagnostic &Diags; SourceManager &SourceMgr; + /// \brief Flag indicating whether or not to collect detailed statistics. + bool CollectStats; + /// \brief Source of additional semantic information. ExternalSemaSource *ExternalSource; @@ -247,6 +218,10 @@ public: /// VisContext - Manages the stack for #pragma GCC visibility. void *VisContext; // Really a "PragmaVisStack*" + /// ExprNeedsCleanups - True if the current evaluation context + /// requires cleanups to be run at its conclusion. + bool ExprNeedsCleanups; + /// \brief Stack containing information about each of the nested /// function, block, and method scopes that are currently active. /// @@ -555,6 +530,9 @@ public: /// \brief The expression evaluation context. ExpressionEvaluationContext Context; + /// \brief Whether the enclosing context needed a cleanup. + bool ParentNeedsCleanups; + /// \brief The number of temporaries that were active when we /// entered this expression evaluation context. unsigned NumTemporaries; @@ -573,8 +551,10 @@ public: PotentiallyEmittedDiagnostics *PotentiallyDiagnosed; ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context, - unsigned NumTemporaries) - : Context(Context), NumTemporaries(NumTemporaries), + unsigned NumTemporaries, + bool ParentNeedsCleanups) + : Context(Context), ParentNeedsCleanups(ParentNeedsCleanups), + NumTemporaries(NumTemporaries), PotentiallyReferenced(0), PotentiallyDiagnosed(0) { } void addReferencedDecl(SourceLocation Loc, Decl *Decl) { @@ -712,7 +692,9 @@ public: ASTContext &getASTContext() const { return Context; } ASTConsumer &getASTConsumer() const { return Consumer; } ASTMutationListener *getASTMutationListener() const; - + + void PrintStats() const; + /// \brief Helper class that creates diagnostics with optional /// template instantiation stacks. /// @@ -766,7 +748,7 @@ public: return FunctionScopes.back(); } - bool hasAnyErrorsInThisFunction() const; + bool hasAnyUnrecoverableErrorsInThisFunction() const; /// \brief Retrieve the current block, if any. sema::BlockScopeInfo *getCurBlock(); @@ -778,7 +760,6 @@ public: // Type Analysis / Processing: SemaType.cpp. // - QualType adjustParameterType(QualType T); QualType BuildQualifiedType(QualType T, SourceLocation Loc, Qualifiers Qs); QualType BuildQualifiedType(QualType T, SourceLocation Loc, unsigned CVR) { return BuildQualifiedType(T, Loc, Qualifiers::fromCVRMask(CVR)); @@ -805,9 +786,8 @@ public: SourceLocation Loc, DeclarationName Entity); QualType BuildParenType(QualType T); - TypeSourceInfo *GetTypeForDeclarator(Declarator &D, Scope *S, - TagDecl **OwnedDecl = 0, - bool AllowAutoInTypeName = false); + TypeSourceInfo *GetTypeForDeclarator(Declarator &D, Scope *S); + TypeSourceInfo *GetTypeForDeclaratorCast(Declarator &D, QualType FromTy); TypeSourceInfo *GetTypeSourceInfoForDeclarator(Declarator &D, QualType T, TypeSourceInfo *ReturnTypeInfo); /// \brief Package the given type and TSI into a ParsedType. @@ -838,7 +818,7 @@ public: const FunctionProtoType *Source, SourceLocation SourceLoc); TypeResult ActOnTypeName(Scope *S, Declarator &D); - + bool RequireCompleteType(SourceLocation Loc, QualType T, const PartialDiagnostic &PD, std::pair<SourceLocation, PartialDiagnostic> Note); @@ -864,7 +844,7 @@ public: // Symbol table / Decl tracking callbacks: SemaDecl.cpp. // - DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr); + DeclGroupPtrTy ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType = 0); void DiagnoseUseOfUnimplementedSelectors(); @@ -983,8 +963,7 @@ public: SourceLocation NameLoc, const Token &NextToken); - Decl *ActOnDeclarator(Scope *S, Declarator &D, - bool IsFunctionDefintion = false); + Decl *ActOnDeclarator(Scope *S, Declarator &D); Decl *HandleDeclarator(Scope *S, Declarator &D, MultiTemplateParamsArg TemplateParameterLists, @@ -1305,13 +1284,20 @@ public: bool IsForUsingDecl); bool IsOverload(FunctionDecl *New, FunctionDecl *Old, bool IsForUsingDecl); - bool TryImplicitConversion(InitializationSequence &Sequence, - const InitializedEntity &Entity, - Expr *From, - bool SuppressUserConversions, - bool AllowExplicit, - bool InOverloadResolution, - bool CStyle); + /// \brief Checks availability of the function depending on the current + /// function context.Inside an unavailable function,unavailability is ignored. + /// + /// \returns true if \arg FD is unavailable and current context is inside + /// an available function, false otherwise. + bool isFunctionConsideredUnavailable(FunctionDecl *FD); + + ImplicitConversionSequence + TryImplicitConversion(Expr *From, QualType ToType, + bool SuppressUserConversions, + bool AllowExplicit, + bool InOverloadResolution, + bool CStyle, + bool AllowObjCWritebackConversion); bool IsIntegralPromotion(Expr *From, QualType FromType, QualType ToType); bool IsFloatingPointPromotion(QualType FromType, QualType ToType); @@ -1321,6 +1307,8 @@ public: QualType& ConvertedType, bool &IncompatibleObjC); bool isObjCPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType, bool &IncompatibleObjC); + bool isObjCWritebackConversion(QualType FromType, QualType ToType, + QualType &ConvertedType); bool IsBlockPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType); bool FunctionArgTypesAreEqual(const FunctionProtoType *OldType, @@ -1338,14 +1326,17 @@ public: CXXCastPath &BasePath, bool IgnoreBaseAccess); bool IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle); + bool CStyle, bool &ObjCLifetimeConversion); + bool IsNoReturnConversion(QualType FromType, QualType ToType, + QualType &ResultTy); bool DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType); ExprResult PerformMoveOrCopyInitialization(const InitializedEntity &Entity, const VarDecl *NRVOCandidate, QualType ResultType, - Expr *Value); + Expr *Value, + bool AllowNRVO = true); bool CanPerformCopyInitialization(const InitializedEntity &Entity, ExprResult Init); @@ -1614,6 +1605,8 @@ public: LookupRedeclarationWithLinkage, /// Look up the name of an Objective-C protocol. LookupObjCProtocolName, + /// Look up implicit 'self' parameter of an objective-c method. + LookupObjCImplicitSelfParam, /// \brief Look up any declaration with any name. LookupAnyName }; @@ -1640,6 +1633,16 @@ private: bool ConstThis, bool VolatileThis); + // \brief The set of known/encountered (unique, canonicalized) NamespaceDecls. + // + // The boolean value will be true to indicate that the namespace was loaded + // from an AST/PCH file, or false otherwise. + llvm::DenseMap<NamespaceDecl*, bool> KnownNamespaces; + + /// \brief Whether we have already loaded known namespaces from an extenal + /// source. + bool LoadedExternalKnownNamespaces; + public: /// \brief Look up a name, looking for a single declaration. Return /// null if the results were absent, ambiguous, or overloaded. @@ -1669,9 +1672,12 @@ public: DeclContextLookupResult LookupConstructors(CXXRecordDecl *Class); CXXConstructorDecl *LookupDefaultConstructor(CXXRecordDecl *Class); - CXXConstructorDecl *LookupCopyConstructor(CXXRecordDecl *Class, - unsigned Quals, - bool *ConstParam = 0); + CXXConstructorDecl *LookupCopyingConstructor(CXXRecordDecl *Class, + unsigned Quals, + bool *ConstParam = 0); + CXXMethodDecl *LookupCopyingAssignment(CXXRecordDecl *Class, unsigned Quals, + bool RValueThis, unsigned ThisQuals, + bool *ConstParam = 0); CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); void ArgumentDependentLookup(DeclarationName Name, bool Operator, @@ -1713,11 +1719,13 @@ public: CTC_ObjCMessageReceiver }; - DeclarationName CorrectTypo(LookupResult &R, Scope *S, CXXScopeSpec *SS, - DeclContext *MemberContext = 0, - bool EnteringContext = false, - CorrectTypoContext CTC = CTC_Unknown, - const ObjCObjectPointerType *OPT = 0); + TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, + Sema::LookupNameKind LookupKind, + Scope *S, CXXScopeSpec *SS, + DeclContext *MemberContext = NULL, + bool EnteringContext = false, + CorrectTypoContext CTC = CTC_Unknown, + const ObjCObjectPointerType *OPT = NULL); void FindAssociatedClassesAndNamespaces(Expr **Args, unsigned NumArgs, AssociatedNamespaceSet &AssociatedNamespaces, @@ -1853,14 +1861,20 @@ public: void AtomicPropertySetterGetterRules(ObjCImplDecl* IMPDecl, ObjCContainerDecl* IDecl); + void DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D); + void DiagnoseDuplicateIvars(ObjCInterfaceDecl *ID, ObjCInterfaceDecl *SID); + enum MethodMatchStrategy { + MMS_loose, + MMS_strict + }; + /// MatchTwoMethodDeclarations - Checks if two methods' type match and returns /// true, or false, accordingly. bool MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, const ObjCMethodDecl *PrevMethod, - bool matchBasedOnSizeAndAlignment = false, - bool matchBasedOnStrictEqulity = false); + MethodMatchStrategy strategy = MMS_strict); /// MatchAllMethodDeclarations - Check methods declaraed in interface or /// or protocol against those declared in their implementations. @@ -2073,10 +2087,13 @@ public: Expr *SynchExpr, Stmt *SynchBody); + StmtResult ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc, Stmt *Body); + VarDecl *BuildExceptionDeclaration(Scope *S, TypeSourceInfo *TInfo, SourceLocation StartLoc, SourceLocation IdLoc, IdentifierInfo *Id); + Decl *ActOnExceptionDeclarator(Scope *S, Declarator &D); StmtResult ActOnCXXCatchBlock(SourceLocation CatchLoc, @@ -2130,6 +2147,9 @@ public: void HandleDelayedDeprecationCheck(sema::DelayedDiagnostic &DD, Decl *Ctx); + bool makeUnavailableInSystemHeader(SourceLocation loc, + llvm::StringRef message); + //===--------------------------------------------------------------------===// // Expression Parsing Callbacks: SemaExpr.cpp. @@ -2146,6 +2166,8 @@ public: void PopExpressionEvaluationContext(); + void DiscardCleanupsInEvaluationContext(); + void MarkDeclarationReferenced(SourceLocation Loc, Decl *D); void MarkDeclarationsReferencedInType(SourceLocation Loc, QualType T); void MarkDeclarationsReferencedInExpr(Expr *E); @@ -2177,6 +2199,11 @@ public: ExprResult ActOnIdExpression(Scope *S, CXXScopeSpec &SS, UnqualifiedId &Name, bool HasTrailingLParen, bool IsAddressOfOperand); + void DecomposeUnqualifiedId(const UnqualifiedId &Id, + TemplateArgumentListInfo &Buffer, + DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *&TemplateArgs); + bool DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, CorrectTypoContext CTC = CTC_Unknown); @@ -2232,8 +2259,7 @@ public: ExprResult ActOnParenExpr(SourceLocation L, SourceLocation R, Expr *Val); ExprResult ActOnParenOrParenListExpr(SourceLocation L, SourceLocation R, - MultiExprArg Val, - ParsedType TypeOfCast = ParsedType()); + MultiExprArg Val); /// ActOnStringLiteral - The specified tokens were lexed as pasted string /// fragments (e.g. "foo" "bar" L"baz"). @@ -2356,21 +2382,19 @@ public: MultiExprArg ExecConfig, SourceLocation GGGLoc); ExprResult ActOnCastExpr(Scope *S, SourceLocation LParenLoc, - ParsedType Ty, SourceLocation RParenLoc, - Expr *Op); + Declarator &D, ParsedType &Ty, + SourceLocation RParenLoc, Expr *Op); ExprResult BuildCStyleCastExpr(SourceLocation LParenLoc, TypeSourceInfo *Ty, SourceLocation RParenLoc, Expr *Op); - bool TypeIsVectorType(ParsedType Ty) { - return GetTypeFromParser(Ty)->isVectorType(); - } + /// \brief Build an altivec or OpenCL literal. + ExprResult BuildVectorLiteral(SourceLocation LParenLoc, + SourceLocation RParenLoc, Expr *E, + TypeSourceInfo *TInfo); ExprResult MaybeConvertParenListExprToParenExpr(Scope *S, Expr *ME); - ExprResult ActOnCastOfParenListExpr(Scope *S, SourceLocation LParenLoc, - SourceLocation RParenLoc, Expr *E, - TypeSourceInfo *TInfo); ExprResult ActOnCompoundLiteral(SourceLocation LParenLoc, ParsedType Ty, @@ -2805,7 +2829,7 @@ public: ExprResult ActOnCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind, SourceLocation LAngleBracketLoc, - ParsedType Ty, + Declarator &D, SourceLocation RAngleBracketLoc, SourceLocation LParenLoc, Expr *E, @@ -2863,8 +2887,11 @@ public: ExprResult ActOnCXXNullPtrLiteral(SourceLocation Loc); //// ActOnCXXThrow - Parse throw expressions. - ExprResult ActOnCXXThrow(SourceLocation OpLoc, Expr *expr); - ExprResult CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E); + ExprResult ActOnCXXThrow(Scope *S, SourceLocation OpLoc, Expr *expr); + ExprResult BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, + bool IsThrownVarInScope); + ExprResult CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E, + bool IsThrownVarInScope); /// ActOnCXXTypeConstructExpr - Parse construction of a specified type. /// Can be interpreted either as function-style casting ("int(x)") @@ -4290,12 +4317,27 @@ public: QualType *FunctionType, sema::TemplateDeductionInfo &Info); + /// brief A function argument from which we performed template argument + // deduction for a call. + struct OriginalCallArg { + OriginalCallArg(QualType OriginalParamType, + unsigned ArgIdx, + QualType OriginalArgType) + : OriginalParamType(OriginalParamType), ArgIdx(ArgIdx), + OriginalArgType(OriginalArgType) { } + + QualType OriginalParamType; + unsigned ArgIdx; + QualType OriginalArgType; + }; + TemplateDeductionResult FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, llvm::SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, - sema::TemplateDeductionInfo &Info); + sema::TemplateDeductionInfo &Info, + llvm::SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs = 0); TemplateDeductionResult DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, @@ -4701,7 +4743,7 @@ public: /// \brief The number of typos corrected by CorrectTypo. unsigned TyposCorrected; - typedef llvm::DenseMap<IdentifierInfo *, std::pair<llvm::StringRef, bool> > + typedef llvm::DenseMap<IdentifierInfo *, TypoCorrection> UnqualifiedTyposCorrectedMap; /// \brief A cache containing the results of typo correction for unqualified @@ -4806,7 +4848,7 @@ public: bool Complain = true); void InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, - Decl *Pattern, Decl *Inst); + const Decl *Pattern, Decl *Inst); bool InstantiateClassTemplateSpecialization(SourceLocation PointOfInstantiation, @@ -4982,6 +5024,15 @@ public: IdentifierInfo *PropertyIvar, SourceLocation PropertyIvarLoc); + enum ObjCSpecialMethodKind { + OSMK_None, + OSMK_Alloc, + OSMK_New, + OSMK_Copy, + OSMK_RetainingInit, + OSMK_NonRetainingInit + }; + struct ObjCArgInfo { IdentifierInfo *Name; SourceLocation NameLoc; @@ -5020,9 +5071,12 @@ public: const ObjCObjectPointerType *OPT, bool IsInstance); + bool inferObjCARCLifetime(ValueDecl *decl); + ExprResult HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, Expr *BaseExpr, + SourceLocation OpLoc, DeclarationName MemberName, SourceLocation MemberLoc, SourceLocation SuperLoc, QualType SuperType, @@ -5098,6 +5152,22 @@ public: SourceLocation RBracLoc, MultiExprArg Args); + ExprResult BuildObjCBridgedCast(SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + TypeSourceInfo *TSInfo, + Expr *SubExpr); + + ExprResult ActOnObjCBridgedCast(Scope *S, + SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + ParsedType Type, + SourceLocation RParenLoc, + Expr *SubExpr); + + bool checkInitMethod(ObjCMethodDecl *method, QualType receiverTypeIfCall); + /// \brief Check whether the given new method is a valid override of the /// given overridden method, and set any properties that should be inherited. /// @@ -5105,7 +5175,7 @@ public: bool CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, const ObjCMethodDecl *Overridden, bool IsImplementation); - + /// \brief Check whether the given method overrides any methods in its class, /// calling \c CheckObjCMethodOverride for each overridden method. bool CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC); @@ -5208,12 +5278,26 @@ public: /// from the inner expression. ExprValueKind CastCategory(Expr *E); + /// \brief The kind of conversion being performed. + enum CheckedConversionKind { + /// \brief An implicit conversion. + CCK_ImplicitConversion, + /// \brief A C-style cast. + CCK_CStyleCast, + /// \brief A functional-style cast. + CCK_FunctionalCast, + /// \brief A cast other than a C-style cast. + CCK_OtherCast + }; + /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit /// cast. If there is already an implicit cast, merge into the existing one. /// If isLvalue, the result of the cast is an lvalue. ExprResult ImpCastExprToType(Expr *E, QualType Type, CastKind CK, ExprValueKind VK = VK_RValue, - const CXXCastPath *BasePath = 0); + const CXXCastPath *BasePath = 0, + CheckedConversionKind CCK + = CCK_ImplicitConversion); /// ScalarTypeToBooleanCastKind - Returns the cast kind corresponding /// to the conversion from scalar type ScalarTy to the Boolean type. @@ -5343,6 +5427,10 @@ public: /// "id <XXX>" = "Foo *", where "Foo *" doesn't implement the XXX protocol. IncompatibleObjCQualifiedId, + /// IncompatibleObjCWeakRef - Assigning a weak-unavailable object to an + /// object with __weak qualifier. + IncompatibleObjCWeakRef, + /// Incompatible - We reject this conversion outright, it is invalid to /// represent it in the AST. Incompatible @@ -5393,11 +5481,12 @@ public: ExprResult PerformImplicitConversion(Expr *From, QualType ToType, const ImplicitConversionSequence& ICS, AssignmentAction Action, - bool CStyle = false); + CheckedConversionKind CCK + = CCK_ImplicitConversion); ExprResult PerformImplicitConversion(Expr *From, QualType ToType, const StandardConversionSequence& SCS, AssignmentAction Action, - bool CStyle); + CheckedConversionKind CCK); /// the following "Check" methods will return a valid/converted QualType /// or a null QualType (indicating an error diagnostic was issued). @@ -5459,7 +5548,8 @@ public: SourceLocation QuestionLoc); /// type checking for vector binary operators. - QualType CheckVectorOperands(SourceLocation l, ExprResult &lex, ExprResult &rex); + QualType CheckVectorOperands(ExprResult &lex, ExprResult &rex, + SourceLocation Loc, bool isCompAssign); QualType CheckVectorCompareOperands(ExprResult &lex, ExprResult &rx, SourceLocation l, bool isRel); @@ -5494,12 +5584,14 @@ public: ReferenceCompareResult CompareReferenceRelationship(SourceLocation Loc, QualType T1, QualType T2, bool &DerivedToBase, - bool &ObjCConversion); + bool &ObjCConversion, + bool &ObjCLifetimeConversion); /// CheckCastTypes - Check type constraints for casting between types under /// C semantics, or forward to CXXCheckCStyleCast in C++. - ExprResult CheckCastTypes(SourceRange TyRange, QualType CastTy, Expr *CastExpr, - CastKind &Kind, ExprValueKind &VK, CXXCastPath &BasePath, + ExprResult CheckCastTypes(SourceLocation CastStartLoc, SourceRange TyRange, + QualType CastTy, Expr *CastExpr, CastKind &Kind, + ExprValueKind &VK, CXXCastPath &BasePath, bool FunctionalStyle = false); ExprResult checkUnknownAnyCast(SourceRange TyRange, QualType castType, @@ -5526,6 +5618,31 @@ public: ExprResult CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, Expr *CastExpr, CastKind &Kind, CXXCastPath &BasePath, bool FunctionalStyle); + + /// \brief Checks for valid expressions which can be cast to an ObjC + /// pointer without needing a bridge cast. + bool ValidObjCARCNoBridgeCastExpr(Expr *&Exp, QualType castType); + + /// \brief Checks for invalid conversions and casts between + /// retainable pointers and other pointer kinds. + void CheckObjCARCConversion(SourceRange castRange, QualType castType, + Expr *&op, CheckedConversionKind CCK); + + bool CheckObjCARCUnavailableWeakConversion(QualType castType, + QualType ExprType); + + /// checkRetainCycles - Check whether an Objective-C message send + /// might create an obvious retain cycle. + void checkRetainCycles(ObjCMessageExpr *msg); + void checkRetainCycles(Expr *receiver, Expr *argument); + + /// checkUnsafeAssigns - Check whether +1 expr is being assigned + /// to weak/__unsafe_unretained type. + bool checkUnsafeAssigns(SourceLocation Loc, QualType LHS, Expr *RHS); + + /// checkUnsafeExprAssigns - Check whether +1 expr is being assigned + /// to weak/__unsafe_unretained expression. + void checkUnsafeExprAssigns(SourceLocation Loc, Expr *LHS, Expr *RHS); /// CheckMessageArgumentTypes - Check types in an Obj-C message send. /// \param Method - May be null. @@ -5544,7 +5661,7 @@ public: QualType getMessageSendResultType(QualType ReceiverType, ObjCMethodDecl *Method, bool isClassMessage, bool isSuperMessage); - + /// \brief If the given expression involves a message send to a method /// with a related result type, emit a note describing what happened. void EmitRelatedResultTypeNote(const Expr *E); @@ -5749,8 +5866,6 @@ public: llvm::SmallVectorImpl<CodeCompletionResult> &Results); //@} - void PrintStats() const {} - //===--------------------------------------------------------------------===// // Extra semantic analysis beyond the C type system @@ -5802,8 +5917,17 @@ private: unsigned format_idx, unsigned firstDataArg, bool isPrintf); - void CheckMemsetcpymoveArguments(const CallExpr *Call, - const IdentifierInfo *FnName); + /// \brief Enumeration used to describe which of the memory setting or copying + /// functions is being checked by \c CheckMemsetcpymoveArguments(). + enum CheckedMemoryFunction { + CMF_Memset, + CMF_Memcpy, + CMF_Memmove + }; + + void CheckMemsetcpymoveArguments(const CallExpr *Call, + CheckedMemoryFunction CMF, + IdentifierInfo *FnName); void CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, SourceLocation ReturnLoc); @@ -5820,10 +5944,20 @@ private: protected: friend class Parser; - friend class InitializationSequence; + friend class InitializationSequence; + friend class ASTReader; + friend class ASTWriter; +public: /// \brief Retrieve the parser's current scope. - Scope *getCurScope() const { return CurScope; } + /// + /// This routine must only be used when it is certain that semantic analysis + /// and the parser are in precisely the same context, which is not the case + /// when, e.g., we are performing any kind of template instantiation. + /// Therefore, the only safe places to use this scope are in the parser + /// itself and in routines directly invoked from the parser and *never* from + /// template substitution or instantiation. + Scope *getCurScope() const { return CurScope; } }; /// \brief RAII object that enters a new expression evaluation context. diff --git a/include/clang/Sema/TypoCorrection.h b/include/clang/Sema/TypoCorrection.h new file mode 100644 index 000000000000..9965953538a1 --- /dev/null +++ b/include/clang/Sema/TypoCorrection.h @@ -0,0 +1,105 @@ +//===--- TypoCorrection.h - Class for typo correction results ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the TypoCorrection class, which stores the results of +// Sema's typo correction (Sema::CorrectTypo). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SEMA_TYPOCORRECTION_H +#define LLVM_CLANG_SEMA_TYPOCORRECTION_H + +#include "clang/AST/DeclCXX.h" + +namespace clang { + +/// @brief Simple class containing the result of Sema::CorrectTypo +class TypoCorrection { +public: + TypoCorrection(const DeclarationName &Name, NamedDecl *NameDecl, + NestedNameSpecifier *NNS=NULL, unsigned distance=0) + : CorrectionName(Name), + CorrectionNameSpec(NNS), + CorrectionDecl(NameDecl), + EditDistance(distance) {} + + TypoCorrection(NamedDecl *Name, NestedNameSpecifier *NNS=NULL, + unsigned distance=0) + : CorrectionName(Name->getDeclName()), + CorrectionNameSpec(NNS), + CorrectionDecl(Name), + EditDistance(distance) {} + + TypoCorrection(DeclarationName Name, NestedNameSpecifier *NNS=NULL, + unsigned distance=0) + : CorrectionName(Name), + CorrectionNameSpec(NNS), + CorrectionDecl(NULL), + EditDistance(distance) {} + + TypoCorrection() + : CorrectionName(), CorrectionNameSpec(NULL), CorrectionDecl(NULL), + EditDistance(0) {} + + /// \brief Gets the DeclarationName of the typo correction + DeclarationName getCorrection() const { return CorrectionName; } + IdentifierInfo* getCorrectionAsIdentifierInfo() const { + return CorrectionName.getAsIdentifierInfo(); + } + + /// \brief Gets the NestedNameSpecifier needed to use the typo correction + NestedNameSpecifier* getCorrectionSpecifier() const { + return CorrectionNameSpec; + } + void setCorrectionSpecifier(NestedNameSpecifier* NNS) { + CorrectionNameSpec = NNS; + } + + /// \brief Gets the "edit distance" of the typo correction from the typo + unsigned getEditDistance() const { return EditDistance; } + + /// \brief Gets the pointer to the declaration of the typo correction + NamedDecl* getCorrectionDecl() const { + return isKeyword() ? NULL : CorrectionDecl; + } + template <class DeclClass> + DeclClass *getCorrectionDeclAs() const { + return dyn_cast_or_null<DeclClass>(getCorrectionDecl()); + } + + void setCorrectionDecl(NamedDecl *CDecl) { + CorrectionDecl = CDecl; + if (!CorrectionName) + CorrectionName = CDecl->getDeclName(); + } + + std::string getAsString(const LangOptions &LO) const; + std::string getQuoted(const LangOptions &LO) const { + return "'" + getAsString(LO) + "'"; + } + + operator bool() const { return bool(CorrectionName); } + + static inline NamedDecl *KeywordDecl() { return (NamedDecl*)-1; } + bool isKeyword() const { return CorrectionDecl == KeywordDecl(); } + + // Returns true if the correction either is a keyword or has a known decl. + bool isResolved() const { return CorrectionDecl != NULL; } + +private: + // Results. + DeclarationName CorrectionName; + NestedNameSpecifier *CorrectionNameSpec; + NamedDecl *CorrectionDecl; + unsigned EditDistance; +}; + +} + +#endif diff --git a/include/clang/Serialization/ASTBitCodes.h b/include/clang/Serialization/ASTBitCodes.h index c881b23ed116..11b8bed903d6 100644 --- a/include/clang/Serialization/ASTBitCodes.h +++ b/include/clang/Serialization/ASTBitCodes.h @@ -371,7 +371,11 @@ namespace clang { /// \brief Record code for the table of offsets into the block /// of file source-location information. - FILE_SOURCE_LOCATION_OFFSETS = 45 + FILE_SOURCE_LOCATION_OFFSETS = 45, + + /// \brief Record code for the set of known namespaces, which are used + /// for typo correction. + KNOWN_NAMESPACES = 46 }; @@ -388,8 +392,8 @@ namespace clang { /// SM_SLOC_BUFFER_ENTRY record. SM_SLOC_BUFFER_BLOB = 3, /// \brief Describes a source location entry (SLocEntry) for a - /// macro instantiation. - SM_SLOC_INSTANTIATION_ENTRY = 4, + /// macro expansion. + SM_SLOC_EXPANSION_ENTRY = 4, /// \brief Describes the SourceManager's line table, with /// information about #line directives. SM_LINE_TABLE = 5 @@ -416,9 +420,8 @@ namespace clang { /// \brief Record types used within a preprocessor detail block. enum PreprocessorDetailRecordTypes { - /// \brief Describes a macro instantiation within the preprocessing - /// record. - PPD_MACRO_INSTANTIATION = 0, + /// \brief Describes a macro expansion within the preprocessing record. + PPD_MACRO_EXPANSION = 0, /// \brief Describes a macro definition within the preprocessing record. PPD_MACRO_DEFINITION = 1, @@ -922,6 +925,8 @@ namespace clang { EXPR_OBJC_MESSAGE_EXPR, /// \brief An ObjCIsa Expr record. EXPR_OBJC_ISA, + /// \breif An ObjCIndirectCopyRestoreExpr record. + EXPR_OBJC_INDIRECT_COPY_RESTORE, /// \brief An ObjCForCollectionStmt record. STMT_OBJC_FOR_COLLECTION, @@ -935,6 +940,8 @@ namespace clang { STMT_OBJC_AT_SYNCHRONIZED, /// \brief An ObjCAtThrowStmt record. STMT_OBJC_AT_THROW, + /// \brief An ObjCAutoreleasePoolStmt record. + STMT_OBJC_AUTORELEASE_POOL, // C++ @@ -968,8 +975,6 @@ namespace clang { EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr EXPR_CXX_TYPEID_EXPR, // CXXTypeidExpr (of expr). EXPR_CXX_TYPEID_TYPE, // CXXTypeidExpr (of type). - EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). - EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). EXPR_CXX_THIS, // CXXThisExpr EXPR_CXX_THROW, // CXXThrowExpr EXPR_CXX_DEFAULT_ARG, // CXXDefaultArgExpr @@ -999,14 +1004,25 @@ namespace clang { EXPR_PACK_EXPANSION, // PackExpansionExpr EXPR_SIZEOF_PACK, // SizeOfPackExpr + EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr - + EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr + // CUDA + EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr - EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr - // OpenCL - EXPR_ASTYPE // An AsTypeExpr record. + EXPR_ASTYPE, // AsTypeExpr + + // Microsoft + EXPR_CXX_UUIDOF_EXPR, // CXXUuidofExpr (of expr). + EXPR_CXX_UUIDOF_TYPE, // CXXUuidofExpr (of type). + STMT_SEH_EXCEPT, // SEHExceptStmt + STMT_SEH_FINALLY, // SEHFinallyStmt + STMT_SEH_TRY, // SEHTryStmt + + // ARC + EXPR_OBJC_BRIDGED_CAST // ObjCBridgedCastExpr }; /// \brief The kinds of designators that can occur in a diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h index 8923e2ab0d71..9e210c3db28c 100644 --- a/include/clang/Serialization/ASTReader.h +++ b/include/clang/Serialization/ASTReader.h @@ -625,6 +625,9 @@ private: /// \brief The OpenCL extension settings. llvm::SmallVector<uint64_t, 1> OpenCLExtensions; + /// \brief A list of the namespaces we've seen. + llvm::SmallVector<uint64_t, 4> KnownNamespaces; + //@} /// \brief Diagnostic IDs and their mappings that the user changed. @@ -1067,7 +1070,7 @@ public: /// /// \returns true if there was an error while reading the /// declarations for this declaration context. - virtual bool FindExternalLexicalDecls(const DeclContext *DC, + virtual ExternalLoadResult FindExternalLexicalDecls(const DeclContext *DC, bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl<Decl*> &Decls); @@ -1125,6 +1128,11 @@ public: virtual std::pair<ObjCMethodList, ObjCMethodList> ReadMethodPool(Selector Sel); + /// \brief Load the set of namespaces that are known to the external source, + /// which will be used during typo correction. + virtual void ReadKnownNamespaces( + llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces); + /// \brief Load a selector from disk, registering its ID if it exists. void LoadSelector(Selector Sel); diff --git a/include/clang/Serialization/ASTWriter.h b/include/clang/Serialization/ASTWriter.h index 78a63abd9eba..18e15014be70 100644 --- a/include/clang/Serialization/ASTWriter.h +++ b/include/clang/Serialization/ASTWriter.h @@ -383,7 +383,7 @@ public: /// are relative to the given system root. /// /// \param PPRec Record of the preprocessing actions that occurred while - /// preprocessing this file, e.g., macro instantiations + /// preprocessing this file, e.g., macro expansions void WriteAST(Sema &SemaRef, MemorizeStatCalls *StatCalls, const std::string &OutputFile, const char* isysroot); diff --git a/include/clang/Serialization/ChainedIncludesSource.h b/include/clang/Serialization/ChainedIncludesSource.h index 0c3e86faf414..f547902ef0eb 100644 --- a/include/clang/Serialization/ChainedIncludesSource.h +++ b/include/clang/Serialization/ChainedIncludesSource.h @@ -47,7 +47,7 @@ protected: virtual DeclContextLookupResult FindExternalVisibleDeclsByName(const DeclContext *DC, DeclarationName Name); virtual void MaterializeVisibleDecls(const DeclContext *DC); - virtual bool FindExternalLexicalDecls(const DeclContext *DC, + virtual ExternalLoadResult FindExternalLexicalDecls(const DeclContext *DC, bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl<Decl*> &Result); virtual void CompleteType(TagDecl *Tag); diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h b/include/clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h index 07cdbf523427..de7b8684066f 100644 --- a/include/clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h +++ b/include/clang/StaticAnalyzer/Core/PathSensitive/GRStateTrait.h @@ -124,6 +124,10 @@ namespace ento { return F.add(K, L); } + static bool Contains(data_type L, key_type K) { + return L.contains(K); + } + static inline data_type MakeData(void* const* p) { return p ? data_type((const llvm::ImmutableListImpl<T>*) *p) : data_type(0); diff --git a/lib/ARCMigrate/ARCMT.cpp b/lib/ARCMigrate/ARCMT.cpp new file mode 100644 index 000000000000..f1d947da677e --- /dev/null +++ b/lib/ARCMigrate/ARCMT.cpp @@ -0,0 +1,541 @@ +//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Internals.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/Utils.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Basic/DiagnosticCategories.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/ADT/Triple.h" + +using namespace clang; +using namespace arcmt; +using llvm::StringRef; + +bool CapturedDiagList::clearDiagnostic(llvm::ArrayRef<unsigned> IDs, + SourceRange range) { + if (range.isInvalid()) + return false; + + bool cleared = false; + ListTy::iterator I = List.begin(); + while (I != List.end()) { + FullSourceLoc diagLoc = I->getLocation(); + if ((IDs.empty() || // empty means clear all diagnostics in the range. + std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && + !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && + (diagLoc == range.getEnd() || + diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) { + cleared = true; + ListTy::iterator eraseS = I++; + while (I != List.end() && I->getLevel() == Diagnostic::Note) + ++I; + // Clear the diagnostic and any notes following it. + List.erase(eraseS, I); + continue; + } + + ++I; + } + + return cleared; +} + +bool CapturedDiagList::hasDiagnostic(llvm::ArrayRef<unsigned> IDs, + SourceRange range) const { + if (range.isInvalid()) + return false; + + ListTy::const_iterator I = List.begin(); + while (I != List.end()) { + FullSourceLoc diagLoc = I->getLocation(); + if ((IDs.empty() || // empty means any diagnostic in the range. + std::find(IDs.begin(), IDs.end(), I->getID()) != IDs.end()) && + !diagLoc.isBeforeInTranslationUnitThan(range.getBegin()) && + (diagLoc == range.getEnd() || + diagLoc.isBeforeInTranslationUnitThan(range.getEnd()))) { + return true; + } + + ++I; + } + + return false; +} + +void CapturedDiagList::reportDiagnostics(Diagnostic &Diags) const { + for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I) + Diags.Report(*I); +} + +bool CapturedDiagList::hasErrors() const { + for (ListTy::const_iterator I = List.begin(), E = List.end(); I != E; ++I) + if (I->getLevel() >= Diagnostic::Error) + return true; + + return false; +} + +namespace { + +class CaptureDiagnosticClient : public DiagnosticClient { + Diagnostic &Diags; + CapturedDiagList &CapturedDiags; +public: + CaptureDiagnosticClient(Diagnostic &diags, + CapturedDiagList &capturedDiags) + : Diags(diags), CapturedDiags(capturedDiags) { } + + virtual void HandleDiagnostic(Diagnostic::Level level, + const DiagnosticInfo &Info) { + if (arcmt::isARCDiagnostic(Info.getID(), Diags) || + level >= Diagnostic::Error || level == Diagnostic::Note) { + CapturedDiags.push_back(StoredDiagnostic(level, Info)); + return; + } + + // Non-ARC warnings are ignored. + Diags.setLastDiagnosticIgnored(); + } +}; + +} // end anonymous namespace + +static inline llvm::StringRef SimulatorVersionDefineName() { + return "__IPHONE_OS_VERSION_MIN_REQUIRED="; +} + +/// \brief Parse the simulator version define: +/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9]) +// and return the grouped values as integers, e.g: +// __IPHONE_OS_VERSION_MIN_REQUIRED=40201 +// will return Major=4, Minor=2, Micro=1. +static bool GetVersionFromSimulatorDefine(llvm::StringRef define, + unsigned &Major, unsigned &Minor, + unsigned &Micro) { + assert(define.startswith(SimulatorVersionDefineName())); + llvm::StringRef name, version; + llvm::tie(name, version) = define.split('='); + if (version.empty()) + return false; + std::string verstr = version.str(); + char *end; + unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10); + if (*end != '\0') + return false; + Major = num / 10000; + num = num % 10000; + Minor = num / 100; + Micro = num % 100; + return true; +} + +static bool HasARCRuntime(CompilerInvocation &origCI) { + // This duplicates some functionality from Darwin::AddDeploymentTarget + // but this function is well defined, so keep it decoupled from the driver + // and avoid unrelated complications. + + for (unsigned i = 0, e = origCI.getPreprocessorOpts().Macros.size(); + i != e; ++i) { + StringRef define = origCI.getPreprocessorOpts().Macros[i].first; + bool isUndef = origCI.getPreprocessorOpts().Macros[i].second; + if (isUndef) + continue; + if (!define.startswith(SimulatorVersionDefineName())) + continue; + unsigned Major, Minor, Micro; + if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) && + Major < 10 && Minor < 100 && Micro < 100) + return Major >= 5; + } + + llvm::Triple triple(origCI.getTargetOpts().Triple); + + if (triple.getOS() == llvm::Triple::IOS) + return triple.getOSMajorVersion() >= 5; + + if (triple.getOS() == llvm::Triple::Darwin) + return triple.getOSMajorVersion() >= 11; + + if (triple.getOS() == llvm::Triple::MacOSX) { + unsigned Major, Minor, Micro; + triple.getOSVersion(Major, Minor, Micro); + return Major > 10 || (Major == 10 && Minor >= 7); + } + + return false; +} + +CompilerInvocation *createInvocationForMigration(CompilerInvocation &origCI) { + llvm::OwningPtr<CompilerInvocation> CInvok; + CInvok.reset(new CompilerInvocation(origCI)); + CInvok->getPreprocessorOpts().ImplicitPCHInclude = std::string(); + CInvok->getPreprocessorOpts().ImplicitPTHInclude = std::string(); + std::string define = getARCMTMacroName(); + define += '='; + CInvok->getPreprocessorOpts().addMacroDef(define); + CInvok->getLangOpts().ObjCAutoRefCount = true; + CInvok->getDiagnosticOpts().ErrorLimit = 0; + CInvok->getDiagnosticOpts().Warnings.push_back( + "error=arc-unsafe-retained-assign"); + CInvok->getLangOpts().ObjCRuntimeHasWeak = HasARCRuntime(origCI); + + return CInvok.take(); +} + +//===----------------------------------------------------------------------===// +// checkForManualIssues. +//===----------------------------------------------------------------------===// + +bool arcmt::checkForManualIssues(CompilerInvocation &origCI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient) { + if (!origCI.getLangOpts().ObjC1) + return false; + + std::vector<TransformFn> transforms = arcmt::getAllTransformations(); + assert(!transforms.empty()); + + llvm::OwningPtr<CompilerInvocation> CInvok; + CInvok.reset(createInvocationForMigration(origCI)); + CInvok->getFrontendOpts().Inputs.clear(); + CInvok->getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename)); + + CapturedDiagList capturedDiags; + + assert(DiagClient); + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags( + new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + + // Filter of all diagnostics. + CaptureDiagnosticClient errRec(*Diags, capturedDiags); + Diags->setClient(&errRec, /*ShouldOwnClient=*/false); + + llvm::OwningPtr<ASTUnit> Unit( + ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags)); + if (!Unit) + return true; + + // Don't filter diagnostics anymore. + Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); + + ASTContext &Ctx = Unit->getASTContext(); + + if (Diags->hasFatalErrorOccurred()) { + Diags->Reset(); + DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + capturedDiags.reportDiagnostics(*Diags); + DiagClient->EndSourceFile(); + return true; + } + + // After parsing of source files ended, we want to reuse the + // diagnostics objects to emit further diagnostics. + // We call BeginSourceFile because DiagnosticClient requires that + // diagnostics with source range information are emitted only in between + // BeginSourceFile() and EndSourceFile(). + DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + + // No macros will be added since we are just checking and we won't modify + // source code. + std::vector<SourceLocation> ARCMTMacroLocs; + + TransformActions testAct(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); + MigrationPass pass(Ctx, Unit->getSema(), testAct, ARCMTMacroLocs); + + for (unsigned i=0, e = transforms.size(); i != e; ++i) + transforms[i](pass); + + capturedDiags.reportDiagnostics(*Diags); + + DiagClient->EndSourceFile(); + + // If we are migrating code that gets the '-fobjc-arc' flag, make sure + // to remove it so that we don't get errors from normal compilation. + origCI.getLangOpts().ObjCAutoRefCount = false; + + return capturedDiags.hasErrors(); +} + +//===----------------------------------------------------------------------===// +// applyTransformations. +//===----------------------------------------------------------------------===// + +static bool applyTransforms(CompilerInvocation &origCI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient, + llvm::StringRef outputDir) { + if (!origCI.getLangOpts().ObjC1) + return false; + + // Make sure checking is successful first. + CompilerInvocation CInvokForCheck(origCI); + if (arcmt::checkForManualIssues(CInvokForCheck, Filename, Kind, DiagClient)) + return true; + + CompilerInvocation CInvok(origCI); + CInvok.getFrontendOpts().Inputs.clear(); + CInvok.getFrontendOpts().Inputs.push_back(std::make_pair(Kind, Filename)); + + MigrationProcess migration(CInvok, DiagClient, outputDir); + + std::vector<TransformFn> transforms = arcmt::getAllTransformations(); + assert(!transforms.empty()); + + for (unsigned i=0, e = transforms.size(); i != e; ++i) { + bool err = migration.applyTransform(transforms[i]); + if (err) return true; + } + + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags( + new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + + if (outputDir.empty()) { + origCI.getLangOpts().ObjCAutoRefCount = true; + return migration.getRemapper().overwriteOriginal(*Diags); + } else { + // If we are migrating code that gets the '-fobjc-arc' flag, make sure + // to remove it so that we don't get errors from normal compilation. + origCI.getLangOpts().ObjCAutoRefCount = false; + return migration.getRemapper().flushToDisk(outputDir, *Diags); + } +} + +bool arcmt::applyTransformations(CompilerInvocation &origCI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient) { + return applyTransforms(origCI, Filename, Kind, DiagClient, llvm::StringRef()); +} + +bool arcmt::migrateWithTemporaryFiles(CompilerInvocation &origCI, + llvm::StringRef Filename, InputKind Kind, + DiagnosticClient *DiagClient, + llvm::StringRef outputDir) { + assert(!outputDir.empty() && "Expected output directory path"); + return applyTransforms(origCI, Filename, Kind, DiagClient, outputDir); +} + +bool arcmt::getFileRemappings(std::vector<std::pair<std::string,std::string> > & + remap, + llvm::StringRef outputDir, + DiagnosticClient *DiagClient) { + assert(!outputDir.empty()); + + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags( + new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + + FileRemapper remapper; + bool err = remapper.initFromDisk(outputDir, *Diags, + /*ignoreIfFilesChanged=*/true); + if (err) + return true; + + CompilerInvocation CI; + remapper.applyMappings(CI); + remap = CI.getPreprocessorOpts().RemappedFiles; + + return false; +} + +//===----------------------------------------------------------------------===// +// CollectTransformActions. +//===----------------------------------------------------------------------===// + +namespace { + +class ARCMTMacroTrackerPPCallbacks : public PPCallbacks { + std::vector<SourceLocation> &ARCMTMacroLocs; + +public: + ARCMTMacroTrackerPPCallbacks(std::vector<SourceLocation> &ARCMTMacroLocs) + : ARCMTMacroLocs(ARCMTMacroLocs) { } + + virtual void MacroExpands(const Token &MacroNameTok, const MacroInfo *MI) { + if (MacroNameTok.getIdentifierInfo()->getName() == getARCMTMacroName()) + ARCMTMacroLocs.push_back(MacroNameTok.getLocation()); + } +}; + +class ARCMTMacroTrackerAction : public ASTFrontendAction { + std::vector<SourceLocation> &ARCMTMacroLocs; + +public: + ARCMTMacroTrackerAction(std::vector<SourceLocation> &ARCMTMacroLocs) + : ARCMTMacroLocs(ARCMTMacroLocs) { } + + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + CI.getPreprocessor().addPPCallbacks( + new ARCMTMacroTrackerPPCallbacks(ARCMTMacroLocs)); + return new ASTConsumer(); + } +}; + +class RewritesApplicator : public TransformActions::RewriteReceiver { + Rewriter &rewriter; + ASTContext &Ctx; + MigrationProcess::RewriteListener *Listener; + +public: + RewritesApplicator(Rewriter &rewriter, ASTContext &ctx, + MigrationProcess::RewriteListener *listener) + : rewriter(rewriter), Ctx(ctx), Listener(listener) { + if (Listener) + Listener->start(ctx); + } + ~RewritesApplicator() { + if (Listener) + Listener->finish(); + } + + virtual void insert(SourceLocation loc, llvm::StringRef text) { + bool err = rewriter.InsertText(loc, text, /*InsertAfter=*/true, + /*indentNewLines=*/true); + if (!err && Listener) + Listener->insert(loc, text); + } + + virtual void remove(CharSourceRange range) { + Rewriter::RewriteOptions removeOpts; + removeOpts.IncludeInsertsAtBeginOfRange = false; + removeOpts.IncludeInsertsAtEndOfRange = false; + removeOpts.RemoveLineIfEmpty = true; + + bool err = rewriter.RemoveText(range, removeOpts); + if (!err && Listener) + Listener->remove(range); + } + + virtual void increaseIndentation(CharSourceRange range, + SourceLocation parentIndent) { + rewriter.IncreaseIndentation(range, parentIndent); + } +}; + +} // end anonymous namespace. + +/// \brief Anchor for VTable. +MigrationProcess::RewriteListener::~RewriteListener() { } + +MigrationProcess::MigrationProcess(const CompilerInvocation &CI, + DiagnosticClient *diagClient, + llvm::StringRef outputDir) + : OrigCI(CI), DiagClient(diagClient) { + if (!outputDir.empty()) { + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags( + new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + Remapper.initFromDisk(outputDir, *Diags, /*ignoreIfFilesChanges=*/true); + } +} + +bool MigrationProcess::applyTransform(TransformFn trans, + RewriteListener *listener) { + llvm::OwningPtr<CompilerInvocation> CInvok; + CInvok.reset(createInvocationForMigration(OrigCI)); + CInvok->getDiagnosticOpts().IgnoreWarnings = true; + + Remapper.applyMappings(*CInvok); + + CapturedDiagList capturedDiags; + std::vector<SourceLocation> ARCMTMacroLocs; + + assert(DiagClient); + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags( + new Diagnostic(DiagID, DiagClient, /*ShouldOwnClient=*/false)); + + // Filter of all diagnostics. + CaptureDiagnosticClient errRec(*Diags, capturedDiags); + Diags->setClient(&errRec, /*ShouldOwnClient=*/false); + + llvm::OwningPtr<ARCMTMacroTrackerAction> ASTAction; + ASTAction.reset(new ARCMTMacroTrackerAction(ARCMTMacroLocs)); + + llvm::OwningPtr<ASTUnit> Unit( + ASTUnit::LoadFromCompilerInvocationAction(CInvok.take(), Diags, + ASTAction.get())); + if (!Unit) + return true; + Unit->setOwnsRemappedFileBuffers(false); // FileRemapper manages that. + + // Don't filter diagnostics anymore. + Diags->setClient(DiagClient, /*ShouldOwnClient=*/false); + + ASTContext &Ctx = Unit->getASTContext(); + + if (Diags->hasFatalErrorOccurred()) { + Diags->Reset(); + DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + capturedDiags.reportDiagnostics(*Diags); + DiagClient->EndSourceFile(); + return true; + } + + // After parsing of source files ended, we want to reuse the + // diagnostics objects to emit further diagnostics. + // We call BeginSourceFile because DiagnosticClient requires that + // diagnostics with source range information are emitted only in between + // BeginSourceFile() and EndSourceFile(). + DiagClient->BeginSourceFile(Ctx.getLangOptions(), &Unit->getPreprocessor()); + + Rewriter rewriter(Ctx.getSourceManager(), Ctx.getLangOptions()); + TransformActions TA(*Diags, capturedDiags, Ctx, Unit->getPreprocessor()); + MigrationPass pass(Ctx, Unit->getSema(), TA, ARCMTMacroLocs); + + trans(pass); + + { + RewritesApplicator applicator(rewriter, Ctx, listener); + TA.applyRewrites(applicator); + } + + DiagClient->EndSourceFile(); + + if (DiagClient->getNumErrors()) + return true; + + for (Rewriter::buffer_iterator + I = rewriter.buffer_begin(), E = rewriter.buffer_end(); I != E; ++I) { + FileID FID = I->first; + RewriteBuffer &buf = I->second; + const FileEntry *file = Ctx.getSourceManager().getFileEntryForID(FID); + assert(file); + std::string newFname = file->getName(); + newFname += "-trans"; + llvm::SmallString<512> newText; + llvm::raw_svector_ostream vecOS(newText); + buf.write(vecOS); + vecOS.flush(); + llvm::MemoryBuffer *memBuf = llvm::MemoryBuffer::getMemBufferCopy( + llvm::StringRef(newText.data(), newText.size()), newFname); + llvm::SmallString<64> filePath(file->getName()); + Unit->getFileManager().FixupRelativePath(filePath); + Remapper.remap(filePath.str(), memBuf); + } + + return false; +} + +//===----------------------------------------------------------------------===// +// isARCDiagnostic. +//===----------------------------------------------------------------------===// + +bool arcmt::isARCDiagnostic(unsigned diagID, Diagnostic &Diag) { + return Diag.getDiagnosticIDs()->getCategoryNumberForDiag(diagID) == + diag::DiagCat_Automatic_Reference_Counting_Issue; +} diff --git a/lib/ARCMigrate/ARCMTActions.cpp b/lib/ARCMigrate/ARCMTActions.cpp new file mode 100644 index 000000000000..345c7452420e --- /dev/null +++ b/lib/ARCMigrate/ARCMTActions.cpp @@ -0,0 +1,53 @@ +//===--- ARCMTActions.cpp - ARC Migrate Tool Frontend Actions ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/ARCMTActions.h" +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/Frontend/CompilerInstance.h" + +using namespace clang; +using namespace arcmt; + +bool CheckAction::BeginInvocation(CompilerInstance &CI) { + if (arcmt::checkForManualIssues(CI.getInvocation(), getCurrentFile(), + getCurrentFileKind(), + CI.getDiagnostics().getClient())) + return false; // errors, stop the action. + + // We only want to see warnings reported from arcmt::checkForManualIssues. + CI.getDiagnostics().setIgnoreAllWarnings(true); + return true; +} + +CheckAction::CheckAction(FrontendAction *WrappedAction) + : WrapperFrontendAction(WrappedAction) {} + +bool ModifyAction::BeginInvocation(CompilerInstance &CI) { + return !arcmt::applyTransformations(CI.getInvocation(), + getCurrentFile(), getCurrentFileKind(), + CI.getDiagnostics().getClient()); +} + +ModifyAction::ModifyAction(FrontendAction *WrappedAction) + : WrapperFrontendAction(WrappedAction) {} + +bool MigrateAction::BeginInvocation(CompilerInstance &CI) { + return !arcmt::migrateWithTemporaryFiles(CI.getInvocation(), + getCurrentFile(), + getCurrentFileKind(), + CI.getDiagnostics().getClient(), + MigrateDir); +} + +MigrateAction::MigrateAction(FrontendAction *WrappedAction, + llvm::StringRef migrateDir) + : WrapperFrontendAction(WrappedAction), MigrateDir(migrateDir) { + if (MigrateDir.empty()) + MigrateDir = "."; // user current directory if none is given. +} diff --git a/lib/ARCMigrate/CMakeLists.txt b/lib/ARCMigrate/CMakeLists.txt new file mode 100644 index 000000000000..5f2711e36f26 --- /dev/null +++ b/lib/ARCMigrate/CMakeLists.txt @@ -0,0 +1,24 @@ +set(LLVM_USED_LIBS clangBasic clangAST clangParse clangFrontend clangRewrite) + +add_clang_library(clangARCMigrate + ARCMT.cpp + ARCMTActions.cpp + FileRemapper.cpp + TransARCAssign.cpp + TransAutoreleasePool.cpp + TransBlockObjCVariable.cpp + TransEmptyStatementsAndDealloc.cpp + TransformActions.cpp + Transforms.cpp + TransProperties.cpp + TransRetainReleaseDealloc.cpp + TransUnbridgedCasts.cpp + TransUnusedInitDelegate.cpp + TransZeroOutPropsInDealloc.cpp + ) + +add_dependencies(clangARCMigrate + ClangAttrClasses + ClangAttrList + ClangDeclNodes + ClangStmtNodes) diff --git a/lib/ARCMigrate/FileRemapper.cpp b/lib/ARCMigrate/FileRemapper.cpp new file mode 100644 index 000000000000..db26c29a3dda --- /dev/null +++ b/lib/ARCMigrate/FileRemapper.cpp @@ -0,0 +1,291 @@ +//===--- FileRemapper.cpp - File Remapping Helper -------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/FileRemapper.h" +#include "clang/Frontend/CompilerInvocation.h" +#include "clang/Basic/FileManager.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <fstream> + +using namespace clang; +using namespace arcmt; + +FileRemapper::FileRemapper() { + FileMgr.reset(new FileManager(FileSystemOptions())); +} + +FileRemapper::~FileRemapper() { + clear(); +} + +void FileRemapper::clear(llvm::StringRef outputDir) { + for (MappingsTy::iterator + I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) + resetTarget(I->second); + FromToMappings.clear(); + assert(ToFromMappings.empty()); + if (!outputDir.empty()) { + std::string infoFile = getRemapInfoFile(outputDir); + bool existed; + llvm::sys::fs::remove(infoFile, existed); + } +} + +std::string FileRemapper::getRemapInfoFile(llvm::StringRef outputDir) { + assert(!outputDir.empty()); + llvm::sys::Path dir(outputDir); + llvm::sys::Path infoFile = dir; + infoFile.appendComponent("remap"); + return infoFile.str(); +} + +bool FileRemapper::initFromDisk(llvm::StringRef outputDir, Diagnostic &Diag, + bool ignoreIfFilesChanged) { + assert(FromToMappings.empty() && + "initFromDisk should be called before any remap calls"); + std::string infoFile = getRemapInfoFile(outputDir); + bool fileExists = false; + llvm::sys::fs::exists(infoFile, fileExists); + if (!fileExists) + return false; + + std::vector<std::pair<const FileEntry *, const FileEntry *> > pairs; + + std::ifstream fin(infoFile.c_str()); + if (!fin.good()) + return report(std::string("Error opening file: ") + infoFile, Diag); + + while (true) { + std::string fromFilename, toFilename; + uint64_t timeModified; + + fin >> fromFilename >> timeModified >> toFilename; + if (fin.eof()) + break; + if (!fin.good()) + return report(std::string("Error in format of file: ") + infoFile, Diag); + + const FileEntry *origFE = FileMgr->getFile(fromFilename); + if (!origFE) { + if (ignoreIfFilesChanged) + continue; + return report(std::string("File does not exist: ") + fromFilename, Diag); + } + const FileEntry *newFE = FileMgr->getFile(toFilename); + if (!newFE) { + if (ignoreIfFilesChanged) + continue; + return report(std::string("File does not exist: ") + toFilename, Diag); + } + + if ((uint64_t)origFE->getModificationTime() != timeModified) { + if (ignoreIfFilesChanged) + continue; + return report(std::string("File was modified: ") + fromFilename, Diag); + } + + pairs.push_back(std::make_pair(origFE, newFE)); + } + + for (unsigned i = 0, e = pairs.size(); i != e; ++i) + remap(pairs[i].first, pairs[i].second); + + return false; +} + +bool FileRemapper::flushToDisk(llvm::StringRef outputDir, Diagnostic &Diag) { + using namespace llvm::sys; + + bool existed; + if (fs::create_directory(outputDir, existed) != llvm::errc::success) + return report(std::string("Could not create directory: ") + outputDir.str(), + Diag); + + std::string errMsg; + std::string infoFile = getRemapInfoFile(outputDir); + llvm::raw_fd_ostream infoOut(infoFile.c_str(), errMsg, + llvm::raw_fd_ostream::F_Binary); + if (!errMsg.empty()) + return report(errMsg, Diag); + + for (MappingsTy::iterator + I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { + + const FileEntry *origFE = I->first; + llvm::SmallString<200> origPath = llvm::StringRef(origFE->getName()); + fs::make_absolute(origPath); + infoOut << origPath << '\n'; + infoOut << (uint64_t)origFE->getModificationTime() << '\n'; + + if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { + llvm::SmallString<200> newPath = llvm::StringRef(FE->getName()); + fs::make_absolute(newPath); + infoOut << newPath << '\n'; + } else { + + llvm::SmallString<64> tempPath; + tempPath = path::filename(origFE->getName()); + tempPath += "-%%%%%%%%"; + tempPath += path::extension(origFE->getName()); + int fd; + if (fs::unique_file(tempPath.str(), fd, tempPath) != llvm::errc::success) + return report(std::string("Could not create file: ") + tempPath.c_str(), + Diag); + + llvm::raw_fd_ostream newOut(fd, /*shouldClose=*/true); + llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); + newOut.write(mem->getBufferStart(), mem->getBufferSize()); + newOut.close(); + + const FileEntry *newE = FileMgr->getFile(tempPath); + remap(origFE, newE); + infoOut << newE->getName() << '\n'; + } + } + + infoOut.close(); + return false; +} + +bool FileRemapper::overwriteOriginal(Diagnostic &Diag, + llvm::StringRef outputDir) { + using namespace llvm::sys; + + for (MappingsTy::iterator + I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { + const FileEntry *origFE = I->first; + if (const FileEntry *newFE = I->second.dyn_cast<const FileEntry *>()) { + if (fs::copy_file(newFE->getName(), origFE->getName(), + fs::copy_option::overwrite_if_exists) != llvm::errc::success) { + std::string err = "Could not copy file '"; + llvm::raw_string_ostream os(err); + os << "Could not copy file '" << newFE->getName() << "' to file '" + << origFE->getName() << "'"; + os.flush(); + return report(err, Diag); + } + } else { + + bool fileExists = false; + fs::exists(origFE->getName(), fileExists); + if (!fileExists) + return report(std::string("File does not exist: ") + origFE->getName(), + Diag); + + std::string errMsg; + llvm::raw_fd_ostream Out(origFE->getName(), errMsg, + llvm::raw_fd_ostream::F_Binary); + if (!errMsg.empty()) + return report(errMsg, Diag); + + llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); + Out.write(mem->getBufferStart(), mem->getBufferSize()); + Out.close(); + } + } + + clear(outputDir); + return false; +} + +void FileRemapper::applyMappings(CompilerInvocation &CI) const { + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + for (MappingsTy::const_iterator + I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { + if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { + PPOpts.addRemappedFile(I->first->getName(), FE->getName()); + } else { + llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); + PPOpts.addRemappedFile(I->first->getName(), mem); + } + } + + PPOpts.RetainRemappedFileBuffers = true; +} + +void FileRemapper::transferMappingsAndClear(CompilerInvocation &CI) { + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + for (MappingsTy::iterator + I = FromToMappings.begin(), E = FromToMappings.end(); I != E; ++I) { + if (const FileEntry *FE = I->second.dyn_cast<const FileEntry *>()) { + PPOpts.addRemappedFile(I->first->getName(), FE->getName()); + } else { + llvm::MemoryBuffer *mem = I->second.get<llvm::MemoryBuffer *>(); + PPOpts.addRemappedFile(I->first->getName(), mem); + } + I->second = Target(); + } + + PPOpts.RetainRemappedFileBuffers = false; + clear(); +} + +void FileRemapper::remap(llvm::StringRef filePath, llvm::MemoryBuffer *memBuf) { + remap(getOriginalFile(filePath), memBuf); +} + +void FileRemapper::remap(llvm::StringRef filePath, llvm::StringRef newPath) { + const FileEntry *file = getOriginalFile(filePath); + const FileEntry *newfile = FileMgr->getFile(newPath); + remap(file, newfile); +} + +void FileRemapper::remap(const FileEntry *file, llvm::MemoryBuffer *memBuf) { + assert(file); + Target &targ = FromToMappings[file]; + resetTarget(targ); + targ = memBuf; +} + +void FileRemapper::remap(const FileEntry *file, const FileEntry *newfile) { + assert(file && newfile); + Target &targ = FromToMappings[file]; + resetTarget(targ); + targ = newfile; + ToFromMappings[newfile] = file; +} + +const FileEntry *FileRemapper::getOriginalFile(llvm::StringRef filePath) { + const FileEntry *file = FileMgr->getFile(filePath); + // If we are updating a file that overriden an original file, + // actually update the original file. + llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator + I = ToFromMappings.find(file); + if (I != ToFromMappings.end()) { + file = I->second; + assert(FromToMappings.find(file) != FromToMappings.end() && + "Original file not in mappings!"); + } + return file; +} + +void FileRemapper::resetTarget(Target &targ) { + if (!targ) + return; + + if (llvm::MemoryBuffer *oldmem = targ.dyn_cast<llvm::MemoryBuffer *>()) { + delete oldmem; + } else { + const FileEntry *toFE = targ.get<const FileEntry *>(); + llvm::DenseMap<const FileEntry *, const FileEntry *>::iterator + I = ToFromMappings.find(toFE); + if (I != ToFromMappings.end()) + ToFromMappings.erase(I); + } +} + +bool FileRemapper::report(const std::string &err, Diagnostic &Diag) { + unsigned ID = Diag.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, + err); + Diag.Report(ID); + return true; +} diff --git a/lib/ARCMigrate/Internals.h b/lib/ARCMigrate/Internals.h new file mode 100644 index 000000000000..4f9b138a06ce --- /dev/null +++ b/lib/ARCMigrate/Internals.h @@ -0,0 +1,148 @@ +//===-- Internals.h - Implementation Details---------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H +#define LLVM_CLANG_LIB_ARCMIGRATE_INTERNALS_H + +#include "clang/ARCMigrate/ARCMT.h" +#include "llvm/ADT/ArrayRef.h" + +namespace clang { + class Sema; + class Stmt; + +namespace arcmt { + +class CapturedDiagList { + typedef std::list<StoredDiagnostic> ListTy; + ListTy List; + +public: + void push_back(const StoredDiagnostic &diag) { List.push_back(diag); } + + bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range); + bool hasDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range) const; + + void reportDiagnostics(Diagnostic &diags) const; + + bool hasErrors() const; +}; + +class TransformActions { + Diagnostic &Diags; + CapturedDiagList &CapturedDiags; + void *Impl; // TransformActionsImpl. + +public: + TransformActions(Diagnostic &diag, CapturedDiagList &capturedDiags, + ASTContext &ctx, Preprocessor &PP); + ~TransformActions(); + + void startTransaction(); + bool commitTransaction(); + void abortTransaction(); + + void insert(SourceLocation loc, llvm::StringRef text); + void insertAfterToken(SourceLocation loc, llvm::StringRef text); + void remove(SourceRange range); + void removeStmt(Stmt *S); + void replace(SourceRange range, llvm::StringRef text); + void replace(SourceRange range, SourceRange replacementRange); + void replaceStmt(Stmt *S, llvm::StringRef text); + void replaceText(SourceLocation loc, llvm::StringRef text, + llvm::StringRef replacementText); + void increaseIndentation(SourceRange range, + SourceLocation parentIndent); + + bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range); + bool clearAllDiagnostics(SourceRange range) { + return clearDiagnostic(llvm::ArrayRef<unsigned>(), range); + } + bool clearDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) { + unsigned IDs[] = { ID1, ID2 }; + return clearDiagnostic(IDs, range); + } + bool clearDiagnostic(unsigned ID1, unsigned ID2, unsigned ID3, + SourceRange range) { + unsigned IDs[] = { ID1, ID2, ID3 }; + return clearDiagnostic(IDs, range); + } + + bool hasDiagnostic(unsigned ID, SourceRange range) { + return CapturedDiags.hasDiagnostic(ID, range); + } + + bool hasDiagnostic(unsigned ID1, unsigned ID2, SourceRange range) { + unsigned IDs[] = { ID1, ID2 }; + return CapturedDiags.hasDiagnostic(IDs, range); + } + + void reportError(llvm::StringRef error, SourceLocation loc, + SourceRange range = SourceRange()); + void reportNote(llvm::StringRef note, SourceLocation loc, + SourceRange range = SourceRange()); + + class RewriteReceiver { + public: + virtual ~RewriteReceiver(); + + virtual void insert(SourceLocation loc, llvm::StringRef text) = 0; + virtual void remove(CharSourceRange range) = 0; + virtual void increaseIndentation(CharSourceRange range, + SourceLocation parentIndent) = 0; + }; + + void applyRewrites(RewriteReceiver &receiver); +}; + +class Transaction { + TransformActions &TA; + bool Aborted; + +public: + Transaction(TransformActions &TA) : TA(TA), Aborted(false) { + TA.startTransaction(); + } + + ~Transaction() { + if (!isAborted()) + TA.commitTransaction(); + } + + void abort() { + TA.abortTransaction(); + Aborted = true; + } + + bool isAborted() const { return Aborted; } +}; + +class MigrationPass { +public: + ASTContext &Ctx; + Sema &SemaRef; + TransformActions &TA; + std::vector<SourceLocation> &ARCMTMacroLocs; + + MigrationPass(ASTContext &Ctx, Sema &sema, TransformActions &TA, + std::vector<SourceLocation> &ARCMTMacroLocs) + : Ctx(Ctx), SemaRef(sema), TA(TA), ARCMTMacroLocs(ARCMTMacroLocs) { } +}; + +bool isARCDiagnostic(unsigned diagID, Diagnostic &Diag); + +static inline llvm::StringRef getARCMTMacroName() { + return "__IMPL_ARCMT_REMOVED_EXPR__"; +} + +} // end namespace arcmt + +} // end namespace clang + +#endif diff --git a/lib/ARCMigrate/Makefile b/lib/ARCMigrate/Makefile new file mode 100644 index 000000000000..5232c5e5aff4 --- /dev/null +++ b/lib/ARCMigrate/Makefile @@ -0,0 +1,18 @@ +##===- clang/lib/ARCMigrate/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements code transformation to ARC mode. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangARCMigrate + +include $(CLANG_LEVEL)/Makefile + diff --git a/lib/ARCMigrate/TransARCAssign.cpp b/lib/ARCMigrate/TransARCAssign.cpp new file mode 100644 index 000000000000..8c00df5daa03 --- /dev/null +++ b/lib/ARCMigrate/TransARCAssign.cpp @@ -0,0 +1,75 @@ +//===--- TransARCAssign.cpp - Tranformations to ARC mode ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// makeAssignARCSafe: +// +// Add '__strong' where appropriate. +// +// for (id x in collection) { +// x = 0; +// } +// ----> +// for (__strong id x in collection) { +// x = 0; +// } +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class ARCAssignChecker : public RecursiveASTVisitor<ARCAssignChecker> { + MigrationPass &Pass; + llvm::DenseSet<VarDecl *> ModifiedVars; + +public: + ARCAssignChecker(MigrationPass &pass) : Pass(pass) { } + + bool VisitBinaryOperator(BinaryOperator *Exp) { + Expr *E = Exp->getLHS(); + SourceLocation OrigLoc = E->getExprLoc(); + SourceLocation Loc = OrigLoc; + DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (declRef && isa<VarDecl>(declRef->getDecl())) { + ASTContext &Ctx = Pass.Ctx; + Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(Ctx, &Loc); + if (IsLV != Expr::MLV_ConstQualified) + return true; + VarDecl *var = cast<VarDecl>(declRef->getDecl()); + if (var->isARCPseudoStrong()) { + Transaction Trans(Pass.TA); + if (Pass.TA.clearDiagnostic(diag::err_typecheck_arr_assign_enumeration, + Exp->getOperatorLoc())) { + if (!ModifiedVars.count(var)) { + TypeLoc TLoc = var->getTypeSourceInfo()->getTypeLoc(); + Pass.TA.insert(TLoc.getBeginLoc(), "__strong "); + ModifiedVars.insert(var); + } + } + } + } + + return true; + } +}; + +} // anonymous namespace + +void trans::makeAssignARCSafe(MigrationPass &pass) { + ARCAssignChecker assignCheck(pass); + assignCheck.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransAutoreleasePool.cpp b/lib/ARCMigrate/TransAutoreleasePool.cpp new file mode 100644 index 000000000000..5b8485432c52 --- /dev/null +++ b/lib/ARCMigrate/TransAutoreleasePool.cpp @@ -0,0 +1,436 @@ +//===--- TransAutoreleasePool.cpp - Tranformations to ARC mode ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// rewriteAutoreleasePool: +// +// Calls to NSAutoreleasePools will be rewritten as an @autorelease scope. +// +// NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +// ... +// [pool release]; +// ----> +// @autorelease { +// ... +// } +// +// An NSAutoreleasePool will not be touched if: +// - There is not a corresponding -release/-drain in the same scope +// - Not all references of the NSAutoreleasePool variable can be removed +// - There is a variable that is declared inside the intended @autorelease scope +// which is also used outside it. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include <map> + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class ReleaseCollector : public RecursiveASTVisitor<ReleaseCollector> { + Decl *Dcl; + llvm::SmallVectorImpl<ObjCMessageExpr *> &Releases; + +public: + ReleaseCollector(Decl *D, llvm::SmallVectorImpl<ObjCMessageExpr *> &releases) + : Dcl(D), Releases(releases) { } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (!E->isInstanceMessage()) + return true; + if (E->getMethodFamily() != OMF_release) + return true; + Expr *instance = E->getInstanceReceiver()->IgnoreParenCasts(); + if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(instance)) { + if (DE->getDecl() == Dcl) + Releases.push_back(E); + } + return true; + } +}; + +} + +namespace { + +class AutoreleasePoolRewriter + : public RecursiveASTVisitor<AutoreleasePoolRewriter> { +public: + AutoreleasePoolRewriter(MigrationPass &pass) + : Body(0), Pass(pass) { + PoolII = &pass.Ctx.Idents.get("NSAutoreleasePool"); + DrainSel = pass.Ctx.Selectors.getNullarySelector( + &pass.Ctx.Idents.get("drain")); + } + + void transformBody(Stmt *body) { + Body = body; + TraverseStmt(body); + } + + ~AutoreleasePoolRewriter() { + llvm::SmallVector<VarDecl *, 8> VarsToHandle; + + for (std::map<VarDecl *, PoolVarInfo>::iterator + I = PoolVars.begin(), E = PoolVars.end(); I != E; ++I) { + VarDecl *var = I->first; + PoolVarInfo &info = I->second; + + // Check that we can handle/rewrite all references of the pool. + + clearRefsIn(info.Dcl, info.Refs); + for (llvm::SmallVectorImpl<PoolScope>::iterator + scpI = info.Scopes.begin(), + scpE = info.Scopes.end(); scpI != scpE; ++scpI) { + PoolScope &scope = *scpI; + clearRefsIn(*scope.Begin, info.Refs); + clearRefsIn(*scope.End, info.Refs); + clearRefsIn(scope.Releases.begin(), scope.Releases.end(), info.Refs); + } + + // Even if one reference is not handled we will not do anything about that + // pool variable. + if (info.Refs.empty()) + VarsToHandle.push_back(var); + } + + for (unsigned i = 0, e = VarsToHandle.size(); i != e; ++i) { + PoolVarInfo &info = PoolVars[VarsToHandle[i]]; + + Transaction Trans(Pass.TA); + + clearUnavailableDiags(info.Dcl); + Pass.TA.removeStmt(info.Dcl); + + // Add "@autoreleasepool { }" + for (llvm::SmallVectorImpl<PoolScope>::iterator + scpI = info.Scopes.begin(), + scpE = info.Scopes.end(); scpI != scpE; ++scpI) { + PoolScope &scope = *scpI; + clearUnavailableDiags(*scope.Begin); + clearUnavailableDiags(*scope.End); + if (scope.IsFollowedBySimpleReturnStmt) { + // Include the return in the scope. + Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {"); + Pass.TA.removeStmt(*scope.End); + Stmt::child_iterator retI = scope.End; + ++retI; + SourceLocation afterSemi = findLocationAfterSemi((*retI)->getLocEnd(), + Pass.Ctx); + assert(afterSemi.isValid() && + "Didn't we check before setting IsFollowedBySimpleReturnStmt " + "to true?"); + Pass.TA.insertAfterToken(afterSemi, "\n}"); + Pass.TA.increaseIndentation( + SourceRange(scope.getIndentedRange().getBegin(), + (*retI)->getLocEnd()), + scope.CompoundParent->getLocStart()); + } else { + Pass.TA.replaceStmt(*scope.Begin, "@autoreleasepool {"); + Pass.TA.replaceStmt(*scope.End, "}"); + Pass.TA.increaseIndentation(scope.getIndentedRange(), + scope.CompoundParent->getLocStart()); + } + } + + // Remove rest of pool var references. + for (llvm::SmallVectorImpl<PoolScope>::iterator + scpI = info.Scopes.begin(), + scpE = info.Scopes.end(); scpI != scpE; ++scpI) { + PoolScope &scope = *scpI; + for (llvm::SmallVectorImpl<ObjCMessageExpr *>::iterator + relI = scope.Releases.begin(), + relE = scope.Releases.end(); relI != relE; ++relI) { + clearUnavailableDiags(*relI); + Pass.TA.removeStmt(*relI); + } + } + } + } + + bool VisitCompoundStmt(CompoundStmt *S) { + llvm::SmallVector<PoolScope, 4> Scopes; + + for (Stmt::child_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) { + Stmt *child = getEssential(*I); + if (DeclStmt *DclS = dyn_cast<DeclStmt>(child)) { + if (DclS->isSingleDecl()) { + if (VarDecl *VD = dyn_cast<VarDecl>(DclS->getSingleDecl())) { + if (isNSAutoreleasePool(VD->getType())) { + PoolVarInfo &info = PoolVars[VD]; + info.Dcl = DclS; + collectRefs(VD, S, info.Refs); + // Does this statement follow the pattern: + // NSAutoreleasePool * pool = [NSAutoreleasePool new]; + if (isPoolCreation(VD->getInit())) { + Scopes.push_back(PoolScope()); + Scopes.back().PoolVar = VD; + Scopes.back().CompoundParent = S; + Scopes.back().Begin = I; + } + } + } + } + } else if (BinaryOperator *bop = dyn_cast<BinaryOperator>(child)) { + if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(bop->getLHS())) { + if (VarDecl *VD = dyn_cast<VarDecl>(dref->getDecl())) { + // Does this statement follow the pattern: + // pool = [NSAutoreleasePool new]; + if (isNSAutoreleasePool(VD->getType()) && + isPoolCreation(bop->getRHS())) { + Scopes.push_back(PoolScope()); + Scopes.back().PoolVar = VD; + Scopes.back().CompoundParent = S; + Scopes.back().Begin = I; + } + } + } + } + + if (Scopes.empty()) + continue; + + if (isPoolDrain(Scopes.back().PoolVar, child)) { + PoolScope &scope = Scopes.back(); + scope.End = I; + handlePoolScope(scope, S); + Scopes.pop_back(); + } + } + return true; + } + +private: + void clearUnavailableDiags(Stmt *S) { + if (S) + Pass.TA.clearDiagnostic(diag::err_unavailable, + diag::err_unavailable_message, + S->getSourceRange()); + } + + struct PoolScope { + VarDecl *PoolVar; + CompoundStmt *CompoundParent; + Stmt::child_iterator Begin; + Stmt::child_iterator End; + bool IsFollowedBySimpleReturnStmt; + llvm::SmallVector<ObjCMessageExpr *, 4> Releases; + + PoolScope() : PoolVar(0), CompoundParent(0), Begin(), End(), + IsFollowedBySimpleReturnStmt(false) { } + + SourceRange getIndentedRange() const { + Stmt::child_iterator rangeS = Begin; + ++rangeS; + if (rangeS == End) + return SourceRange(); + Stmt::child_iterator rangeE = Begin; + for (Stmt::child_iterator I = rangeS; I != End; ++I) + ++rangeE; + return SourceRange((*rangeS)->getLocStart(), (*rangeE)->getLocEnd()); + } + }; + + class NameReferenceChecker : public RecursiveASTVisitor<NameReferenceChecker>{ + ASTContext &Ctx; + SourceRange ScopeRange; + SourceLocation &referenceLoc, &declarationLoc; + + public: + NameReferenceChecker(ASTContext &ctx, PoolScope &scope, + SourceLocation &referenceLoc, + SourceLocation &declarationLoc) + : Ctx(ctx), referenceLoc(referenceLoc), + declarationLoc(declarationLoc) { + ScopeRange = SourceRange((*scope.Begin)->getLocStart(), + (*scope.End)->getLocStart()); + } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + return checkRef(E->getLocation(), E->getDecl()->getLocation()); + } + + bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + return checkRef(E->getLocation(), E->getDecl()->getLocation()); + } + + bool VisitTypedefTypeLoc(TypedefTypeLoc TL) { + return checkRef(TL.getBeginLoc(), TL.getTypedefNameDecl()->getLocation()); + } + + bool VisitTagTypeLoc(TagTypeLoc TL) { + return checkRef(TL.getBeginLoc(), TL.getDecl()->getLocation()); + } + + private: + bool checkRef(SourceLocation refLoc, SourceLocation declLoc) { + if (isInScope(declLoc)) { + referenceLoc = refLoc; + declarationLoc = declLoc; + return false; + } + return true; + } + + bool isInScope(SourceLocation loc) { + SourceManager &SM = Ctx.getSourceManager(); + if (SM.isBeforeInTranslationUnit(loc, ScopeRange.getBegin())) + return false; + return SM.isBeforeInTranslationUnit(loc, ScopeRange.getEnd()); + } + }; + + void handlePoolScope(PoolScope &scope, CompoundStmt *compoundS) { + // Check that all names declared inside the scope are not used + // outside the scope. + { + bool nameUsedOutsideScope = false; + SourceLocation referenceLoc, declarationLoc; + Stmt::child_iterator SI = scope.End, SE = compoundS->body_end(); + ++SI; + // Check if the autoreleasepool scope is followed by a simple return + // statement, in which case we will include the return in the scope. + if (SI != SE) + if (ReturnStmt *retS = dyn_cast<ReturnStmt>(*SI)) + if ((retS->getRetValue() == 0 || + isa<DeclRefExpr>(retS->getRetValue()->IgnoreParenCasts())) && + findLocationAfterSemi(retS->getLocEnd(), Pass.Ctx).isValid()) { + scope.IsFollowedBySimpleReturnStmt = true; + ++SI; // the return will be included in scope, don't check it. + } + + for (; SI != SE; ++SI) { + nameUsedOutsideScope = !NameReferenceChecker(Pass.Ctx, scope, + referenceLoc, + declarationLoc).TraverseStmt(*SI); + if (nameUsedOutsideScope) + break; + } + + // If not all references were cleared it means some variables/typenames/etc + // declared inside the pool scope are used outside of it. + // We won't try to rewrite the pool. + if (nameUsedOutsideScope) { + Pass.TA.reportError("a name is referenced outside the " + "NSAutoreleasePool scope that it was declared in", referenceLoc); + Pass.TA.reportNote("name declared here", declarationLoc); + Pass.TA.reportNote("intended @autoreleasepool scope begins here", + (*scope.Begin)->getLocStart()); + Pass.TA.reportNote("intended @autoreleasepool scope ends here", + (*scope.End)->getLocStart()); + return; + } + } + + // Collect all releases of the pool; they will be removed. + { + ReleaseCollector releaseColl(scope.PoolVar, scope.Releases); + Stmt::child_iterator I = scope.Begin; + ++I; + for (; I != scope.End; ++I) + releaseColl.TraverseStmt(*I); + } + + PoolVars[scope.PoolVar].Scopes.push_back(scope); + } + + bool isPoolCreation(Expr *E) { + if (!E) return false; + E = getEssential(E); + ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E); + if (!ME) return false; + if (ME->getMethodFamily() == OMF_new && + ME->getReceiverKind() == ObjCMessageExpr::Class && + isNSAutoreleasePool(ME->getReceiverInterface())) + return true; + if (ME->getReceiverKind() == ObjCMessageExpr::Instance && + ME->getMethodFamily() == OMF_init) { + Expr *rec = getEssential(ME->getInstanceReceiver()); + if (ObjCMessageExpr *recME = dyn_cast_or_null<ObjCMessageExpr>(rec)) { + if (recME->getMethodFamily() == OMF_alloc && + recME->getReceiverKind() == ObjCMessageExpr::Class && + isNSAutoreleasePool(recME->getReceiverInterface())) + return true; + } + } + + return false; + } + + bool isPoolDrain(VarDecl *poolVar, Stmt *S) { + if (!S) return false; + S = getEssential(S); + ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S); + if (!ME) return false; + if (ME->getReceiverKind() == ObjCMessageExpr::Instance) { + Expr *rec = getEssential(ME->getInstanceReceiver()); + if (DeclRefExpr *dref = dyn_cast<DeclRefExpr>(rec)) + if (dref->getDecl() == poolVar) + return ME->getMethodFamily() == OMF_release || + ME->getSelector() == DrainSel; + } + + return false; + } + + bool isNSAutoreleasePool(ObjCInterfaceDecl *IDecl) { + return IDecl && IDecl->getIdentifier() == PoolII; + } + + bool isNSAutoreleasePool(QualType Ty) { + QualType pointee = Ty->getPointeeType(); + if (pointee.isNull()) + return false; + if (const ObjCInterfaceType *interT = pointee->getAs<ObjCInterfaceType>()) + return isNSAutoreleasePool(interT->getDecl()); + return false; + } + + static Expr *getEssential(Expr *E) { + return cast<Expr>(getEssential((Stmt*)E)); + } + static Stmt *getEssential(Stmt *S) { + if (ExprWithCleanups *EWC = dyn_cast<ExprWithCleanups>(S)) + S = EWC->getSubExpr(); + if (Expr *E = dyn_cast<Expr>(S)) + S = E->IgnoreParenCasts(); + return S; + } + + Stmt *Body; + MigrationPass &Pass; + + IdentifierInfo *PoolII; + Selector DrainSel; + + struct PoolVarInfo { + DeclStmt *Dcl; + ExprSet Refs; + llvm::SmallVector<PoolScope, 2> Scopes; + + PoolVarInfo() : Dcl(0) { } + }; + + std::map<VarDecl *, PoolVarInfo> PoolVars; +}; + +} // anonymous namespace + +void trans::rewriteAutoreleasePool(MigrationPass &pass) { + BodyTransform<AutoreleasePoolRewriter> trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransBlockObjCVariable.cpp b/lib/ARCMigrate/TransBlockObjCVariable.cpp new file mode 100644 index 000000000000..0e342b7a8f8c --- /dev/null +++ b/lib/ARCMigrate/TransBlockObjCVariable.cpp @@ -0,0 +1,143 @@ +//===--- TransBlockObjCVariable.cpp - Tranformations to ARC mode ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// rewriteBlockObjCVariable: +// +// Adding __block to an obj-c variable could be either because the the variable +// is used for output storage or the user wanted to break a retain cycle. +// This transformation checks whether a reference of the variable for the block +// is actually needed (it is assigned to or its address is taken) or not. +// If the reference is not needed it will assume __block was added to break a +// cycle so it will remove '__block' and add __weak/__unsafe_unretained. +// e.g +// +// __block Foo *x; +// bar(^ { [x cake]; }); +// ----> +// __weak Foo *x; +// bar(^ { [x cake]; }); +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class RootBlockObjCVarRewriter : + public RecursiveASTVisitor<RootBlockObjCVarRewriter> { + MigrationPass &Pass; + llvm::DenseSet<VarDecl *> CheckedVars; + + class BlockVarChecker : public RecursiveASTVisitor<BlockVarChecker> { + VarDecl *Var; + + typedef RecursiveASTVisitor<BlockVarChecker> base; + public: + BlockVarChecker(VarDecl *var) : Var(var) { } + + bool TraverseImplicitCastExpr(ImplicitCastExpr *castE) { + if (BlockDeclRefExpr * + ref = dyn_cast<BlockDeclRefExpr>(castE->getSubExpr())) { + if (ref->getDecl() == Var) { + if (castE->getCastKind() == CK_LValueToRValue) + return true; // Using the value of the variable. + if (castE->getCastKind() == CK_NoOp && castE->isLValue() && + Var->getASTContext().getLangOptions().CPlusPlus) + return true; // Binding to const C++ reference. + } + } + + return base::TraverseImplicitCastExpr(castE); + } + + bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + if (E->getDecl() == Var) + return false; // The reference of the variable, and not just its value, + // is needed. + return true; + } + }; + +public: + RootBlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { } + + bool VisitBlockDecl(BlockDecl *block) { + llvm::SmallVector<VarDecl *, 4> BlockVars; + + for (BlockDecl::capture_iterator + I = block->capture_begin(), E = block->capture_end(); I != E; ++I) { + VarDecl *var = I->getVariable(); + if (I->isByRef() && + !isAlreadyChecked(var) && + var->getType()->isObjCObjectPointerType() && + isImplicitStrong(var->getType())) { + BlockVars.push_back(var); + } + } + + for (unsigned i = 0, e = BlockVars.size(); i != e; ++i) { + VarDecl *var = BlockVars[i]; + CheckedVars.insert(var); + + BlockVarChecker checker(var); + bool onlyValueOfVarIsNeeded = checker.TraverseStmt(block->getBody()); + if (onlyValueOfVarIsNeeded) { + BlocksAttr *attr = var->getAttr<BlocksAttr>(); + if(!attr) + continue; + bool useWeak = canApplyWeak(Pass.Ctx, var->getType()); + SourceManager &SM = Pass.Ctx.getSourceManager(); + Transaction Trans(Pass.TA); + Pass.TA.replaceText(SM.getInstantiationLoc(attr->getLocation()), + "__block", + useWeak ? "__weak" : "__unsafe_unretained"); + } + + } + + return true; + } + +private: + bool isAlreadyChecked(VarDecl *VD) { + return CheckedVars.count(VD); + } + + bool isImplicitStrong(QualType ty) { + if (isa<AttributedType>(ty.getTypePtr())) + return false; + return ty.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong; + } +}; + +class BlockObjCVarRewriter : public RecursiveASTVisitor<BlockObjCVarRewriter> { + MigrationPass &Pass; + +public: + BlockObjCVarRewriter(MigrationPass &pass) : Pass(pass) { } + + bool TraverseBlockDecl(BlockDecl *block) { + RootBlockObjCVarRewriter(Pass).TraverseDecl(block); + return true; + } +}; + +} // anonymous namespace + +void trans::rewriteBlockObjCVariable(MigrationPass &pass) { + BlockObjCVarRewriter trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp new file mode 100644 index 000000000000..d0bc332ff160 --- /dev/null +++ b/lib/ARCMigrate/TransEmptyStatementsAndDealloc.cpp @@ -0,0 +1,211 @@ +//===--- TransEmptyStatements.cpp - Tranformations to ARC mode ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// removeEmptyStatementsAndDealloc: +// +// Removes empty statements that are leftovers from previous transformations. +// e.g for +// +// [x retain]; +// +// removeRetainReleaseDealloc will leave an empty ";" that removeEmptyStatements +// will remove. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/AST/StmtVisitor.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +/// \brief Returns true if the statement became empty due to previous +/// transformations. +class EmptyChecker : public StmtVisitor<EmptyChecker, bool> { + ASTContext &Ctx; + llvm::DenseSet<unsigned> &MacroLocs; + +public: + EmptyChecker(ASTContext &ctx, llvm::DenseSet<unsigned> ¯oLocs) + : Ctx(ctx), MacroLocs(macroLocs) { } + + bool VisitNullStmt(NullStmt *S) { + return isMacroLoc(S->getLeadingEmptyMacroLoc()); + } + bool VisitCompoundStmt(CompoundStmt *S) { + if (S->body_empty()) + return false; // was already empty, not because of transformations. + for (CompoundStmt::body_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) + if (!Visit(*I)) + return false; + return true; + } + bool VisitIfStmt(IfStmt *S) { + if (S->getConditionVariable()) + return false; + Expr *condE = S->getCond(); + if (!condE) + return false; + if (hasSideEffects(condE, Ctx)) + return false; + if (!S->getThen() || !Visit(S->getThen())) + return false; + if (S->getElse() && !Visit(S->getElse())) + return false; + return true; + } + bool VisitWhileStmt(WhileStmt *S) { + if (S->getConditionVariable()) + return false; + Expr *condE = S->getCond(); + if (!condE) + return false; + if (hasSideEffects(condE, Ctx)) + return false; + if (!S->getBody()) + return false; + return Visit(S->getBody()); + } + bool VisitDoStmt(DoStmt *S) { + Expr *condE = S->getCond(); + if (!condE) + return false; + if (hasSideEffects(condE, Ctx)) + return false; + if (!S->getBody()) + return false; + return Visit(S->getBody()); + } + bool VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { + Expr *Exp = S->getCollection(); + if (!Exp) + return false; + if (hasSideEffects(Exp, Ctx)) + return false; + if (!S->getBody()) + return false; + return Visit(S->getBody()); + } + bool VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + if (!S->getSubStmt()) + return false; + return Visit(S->getSubStmt()); + } + +private: + bool isMacroLoc(SourceLocation loc) { + if (loc.isInvalid()) return false; + return MacroLocs.count(loc.getRawEncoding()); + } +}; + +class EmptyStatementsRemover : + public RecursiveASTVisitor<EmptyStatementsRemover> { + MigrationPass &Pass; + llvm::DenseSet<unsigned> &MacroLocs; + +public: + EmptyStatementsRemover(MigrationPass &pass, + llvm::DenseSet<unsigned> ¯oLocs) + : Pass(pass), MacroLocs(macroLocs) { } + + bool TraverseStmtExpr(StmtExpr *E) { + CompoundStmt *S = E->getSubStmt(); + for (CompoundStmt::body_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) { + if (I != E - 1) + check(*I); + TraverseStmt(*I); + } + return true; + } + + bool VisitCompoundStmt(CompoundStmt *S) { + for (CompoundStmt::body_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) + check(*I); + return true; + } + + bool isMacroLoc(SourceLocation loc) { + if (loc.isInvalid()) return false; + return MacroLocs.count(loc.getRawEncoding()); + } + + ASTContext &getContext() { return Pass.Ctx; } + +private: + void check(Stmt *S) { + if (!S) return; + if (EmptyChecker(Pass.Ctx, MacroLocs).Visit(S)) { + Transaction Trans(Pass.TA); + Pass.TA.removeStmt(S); + } + } +}; + +} // anonymous namespace + +static bool isBodyEmpty(CompoundStmt *body, + ASTContext &Ctx, llvm::DenseSet<unsigned> &MacroLocs) { + for (CompoundStmt::body_iterator + I = body->body_begin(), E = body->body_end(); I != E; ++I) + if (!EmptyChecker(Ctx, MacroLocs).Visit(*I)) + return false; + + return true; +} + +static void removeDeallocMethod(MigrationPass &pass, + llvm::DenseSet<unsigned> &MacroLocs) { + ASTContext &Ctx = pass.Ctx; + TransformActions &TA = pass.TA; + DeclContext *DC = Ctx.getTranslationUnitDecl(); + + typedef DeclContext::specific_decl_iterator<ObjCImplementationDecl> + impl_iterator; + for (impl_iterator I = impl_iterator(DC->decls_begin()), + E = impl_iterator(DC->decls_end()); I != E; ++I) { + for (ObjCImplementationDecl::instmeth_iterator + MI = (*I)->instmeth_begin(), + ME = (*I)->instmeth_end(); MI != ME; ++MI) { + ObjCMethodDecl *MD = *MI; + if (MD->getMethodFamily() == OMF_dealloc) { + if (MD->hasBody() && + isBodyEmpty(MD->getCompoundBody(), Ctx, MacroLocs)) { + Transaction Trans(TA); + TA.remove(MD->getSourceRange()); + } + break; + } + } + } +} + +void trans::removeEmptyStatementsAndDealloc(MigrationPass &pass) { + llvm::DenseSet<unsigned> MacroLocs; + for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) + MacroLocs.insert(pass.ARCMTMacroLocs[i].getRawEncoding()); + + EmptyStatementsRemover(pass, MacroLocs) + .TraverseDecl(pass.Ctx.getTranslationUnitDecl()); + + removeDeallocMethod(pass, MacroLocs); + + for (unsigned i = 0, e = pass.ARCMTMacroLocs.size(); i != e; ++i) { + Transaction Trans(pass.TA); + pass.TA.remove(pass.ARCMTMacroLocs[i]); + } +} diff --git a/lib/ARCMigrate/TransProperties.cpp b/lib/ARCMigrate/TransProperties.cpp new file mode 100644 index 000000000000..872c95e1a444 --- /dev/null +++ b/lib/ARCMigrate/TransProperties.cpp @@ -0,0 +1,364 @@ +//===--- TransProperties.cpp - Tranformations to ARC mode -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// rewriteProperties: +// +// - Adds strong/weak/unsafe_unretained ownership specifier to properties that +// are missing one. +// - Migrates properties from (retain) to (strong) and (assign) to +// (unsafe_unretained/weak). +// - If a property is synthesized, adds the ownership specifier in the ivar +// backing the property. +// +// @interface Foo : NSObject { +// NSObject *x; +// } +// @property (assign) id x; +// @end +// ----> +// @interface Foo : NSObject { +// NSObject *__weak x; +// } +// @property (weak) id x; +// @end +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include <map> + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class PropertiesRewriter { + MigrationPass &Pass; + + struct PropData { + ObjCPropertyDecl *PropD; + ObjCIvarDecl *IvarD; + ObjCPropertyImplDecl *ImplD; + + PropData(ObjCPropertyDecl *propD) : PropD(propD), IvarD(0), ImplD(0) { } + }; + + typedef llvm::SmallVector<PropData, 2> PropsTy; + typedef std::map<unsigned, PropsTy> AtPropDeclsTy; + AtPropDeclsTy AtProps; + +public: + PropertiesRewriter(MigrationPass &pass) : Pass(pass) { } + + void doTransform(ObjCImplementationDecl *D) { + ObjCInterfaceDecl *iface = D->getClassInterface(); + if (!iface) + return; + + for (ObjCInterfaceDecl::prop_iterator + propI = iface->prop_begin(), + propE = iface->prop_end(); propI != propE; ++propI) { + if (propI->getAtLoc().isInvalid()) + continue; + PropsTy &props = AtProps[propI->getAtLoc().getRawEncoding()]; + props.push_back(*propI); + } + + typedef DeclContext::specific_decl_iterator<ObjCPropertyImplDecl> + prop_impl_iterator; + for (prop_impl_iterator + I = prop_impl_iterator(D->decls_begin()), + E = prop_impl_iterator(D->decls_end()); I != E; ++I) { + ObjCPropertyImplDecl *implD = *I; + if (implD->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + ObjCPropertyDecl *propD = implD->getPropertyDecl(); + if (!propD || propD->isInvalidDecl()) + continue; + ObjCIvarDecl *ivarD = implD->getPropertyIvarDecl(); + if (!ivarD || ivarD->isInvalidDecl()) + continue; + unsigned rawAtLoc = propD->getAtLoc().getRawEncoding(); + AtPropDeclsTy::iterator findAtLoc = AtProps.find(rawAtLoc); + if (findAtLoc == AtProps.end()) + continue; + + PropsTy &props = findAtLoc->second; + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (I->PropD == propD) { + I->IvarD = ivarD; + I->ImplD = implD; + break; + } + } + } + + for (AtPropDeclsTy::iterator + I = AtProps.begin(), E = AtProps.end(); I != E; ++I) { + SourceLocation atLoc = SourceLocation::getFromRawEncoding(I->first); + PropsTy &props = I->second; + QualType ty = getPropertyType(props); + if (!ty->isObjCRetainableType()) + continue; + if (hasIvarWithExplicitOwnership(props)) + continue; + + Transaction Trans(Pass.TA); + rewriteProperty(props, atLoc); + } + } + +private: + void rewriteProperty(PropsTy &props, SourceLocation atLoc) const { + ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + + if (propAttrs & (ObjCPropertyDecl::OBJC_PR_copy | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_weak)) + return; + + if (propAttrs & ObjCPropertyDecl::OBJC_PR_retain) { + rewriteAttribute("retain", "strong", atLoc); + return; + } + + if (propAttrs & ObjCPropertyDecl::OBJC_PR_assign) + return rewriteAssign(props, atLoc); + + return maybeAddWeakOrUnsafeUnretainedAttr(props, atLoc); + } + + void rewriteAssign(PropsTy &props, SourceLocation atLoc) const { + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); + + bool rewroteAttr = rewriteAttribute("assign", + canUseWeak ? "weak" : "unsafe_unretained", + atLoc); + if (!rewroteAttr) + canUseWeak = false; + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) + Pass.TA.insert(I->IvarD->getLocation(), + canUseWeak ? "__weak " : "__unsafe_unretained "); + if (I->ImplD) + Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, + I->ImplD->getLocation()); + } + } + + void maybeAddWeakOrUnsafeUnretainedAttr(PropsTy &props, + SourceLocation atLoc) const { + ObjCPropertyDecl::PropertyAttributeKind propAttrs = getPropertyAttrs(props); + if ((propAttrs & ObjCPropertyDecl::OBJC_PR_readonly) && + hasNoBackingIvars(props)) + return; + + bool canUseWeak = canApplyWeak(Pass.Ctx, getPropertyType(props)); + bool addedAttr = addAttribute(canUseWeak ? "weak" : "unsafe_unretained", + atLoc); + if (!addedAttr) + canUseWeak = false; + + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) + Pass.TA.insert(I->IvarD->getLocation(), + canUseWeak ? "__weak " : "__unsafe_unretained "); + if (I->ImplD) { + Pass.TA.clearDiagnostic(diag::err_arc_assign_property_ownership, + I->ImplD->getLocation()); + Pass.TA.clearDiagnostic( + diag::err_arc_objc_property_default_assign_on_object, + I->ImplD->getLocation()); + } + } + } + + bool rewriteAttribute(llvm::StringRef fromAttr, llvm::StringRef toAttr, + SourceLocation atLoc) const { + if (atLoc.isMacroID()) + return false; + + SourceManager &SM = Pass.Ctx.getSourceManager(); + + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); + + // Try to load the file buffer. + bool invalidTemp = false; + llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; + + const char *tokenBegin = file.data() + locInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Pass.Ctx.getLangOptions(), + file.begin(), tokenBegin, file.end()); + Token tok; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::at)) return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::raw_identifier)) return false; + if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength()) + != "property") + return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::l_paren)) return false; + + lexer.LexFromRawLexer(tok); + if (tok.is(tok::r_paren)) + return false; + + while (1) { + if (tok.isNot(tok::raw_identifier)) return false; + llvm::StringRef ident(tok.getRawIdentifierData(), tok.getLength()); + if (ident == fromAttr) { + Pass.TA.replaceText(tok.getLocation(), fromAttr, toAttr); + return true; + } + + do { + lexer.LexFromRawLexer(tok); + } while (tok.isNot(tok::comma) && tok.isNot(tok::r_paren)); + if (tok.is(tok::r_paren)) + break; + lexer.LexFromRawLexer(tok); + } + + return false; + } + + bool addAttribute(llvm::StringRef attr, SourceLocation atLoc) const { + if (atLoc.isMacroID()) + return false; + + SourceManager &SM = Pass.Ctx.getSourceManager(); + + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(atLoc); + + // Try to load the file buffer. + bool invalidTemp = false; + llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; + + const char *tokenBegin = file.data() + locInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Pass.Ctx.getLangOptions(), + file.begin(), tokenBegin, file.end()); + Token tok; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::at)) return false; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::raw_identifier)) return false; + if (llvm::StringRef(tok.getRawIdentifierData(), tok.getLength()) + != "property") + return false; + lexer.LexFromRawLexer(tok); + + if (tok.isNot(tok::l_paren)) { + Pass.TA.insert(tok.getLocation(), std::string("(") + attr.str() + ") "); + return true; + } + + lexer.LexFromRawLexer(tok); + if (tok.is(tok::r_paren)) { + Pass.TA.insert(tok.getLocation(), attr); + return true; + } + + if (tok.isNot(tok::raw_identifier)) return false; + + Pass.TA.insert(tok.getLocation(), std::string(attr) + ", "); + return true; + } + + bool hasIvarWithExplicitOwnership(PropsTy &props) const { + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) { + if (isUserDeclared(I->IvarD)) { + if (isa<AttributedType>(I->IvarD->getType())) + return true; + if (I->IvarD->getType().getLocalQualifiers().getObjCLifetime() + != Qualifiers::OCL_Strong) + return true; + } + } + + return false; + } + + bool hasNoBackingIvars(PropsTy &props) const { + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + if (I->IvarD) + return false; + + return true; + } + + bool isUserDeclared(ObjCIvarDecl *ivarD) const { + return ivarD && !ivarD->getSynthesize(); + } + + QualType getPropertyType(PropsTy &props) const { + assert(!props.empty()); + QualType ty = props[0].PropD->getType(); + +#ifndef NDEBUG + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + assert(ty == I->PropD->getType()); +#endif + + return ty; + } + + ObjCPropertyDecl::PropertyAttributeKind + getPropertyAttrs(PropsTy &props) const { + assert(!props.empty()); + ObjCPropertyDecl::PropertyAttributeKind + attrs = props[0].PropD->getPropertyAttributesAsWritten(); + +#ifndef NDEBUG + for (PropsTy::iterator I = props.begin(), E = props.end(); I != E; ++I) + assert(attrs == I->PropD->getPropertyAttributesAsWritten()); +#endif + + return attrs; + } +}; + +class ImplementationChecker : + public RecursiveASTVisitor<ImplementationChecker> { + MigrationPass &Pass; + +public: + ImplementationChecker(MigrationPass &pass) : Pass(pass) { } + + bool TraverseObjCImplementationDecl(ObjCImplementationDecl *D) { + PropertiesRewriter(Pass).doTransform(D); + return true; + } +}; + +} // anonymous namespace + +void trans::rewriteProperties(MigrationPass &pass) { + ImplementationChecker(pass).TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransRetainReleaseDealloc.cpp b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp new file mode 100644 index 000000000000..ed6ed0adfdf2 --- /dev/null +++ b/lib/ARCMigrate/TransRetainReleaseDealloc.cpp @@ -0,0 +1,219 @@ +//===--- TransRetainReleaseDealloc.cpp - Tranformations to ARC mode -------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// removeRetainReleaseDealloc: +// +// Removes retain/release/autorelease/dealloc messages. +// +// return [[foo retain] autorelease]; +// ----> +// return foo; +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class RetainReleaseDeallocRemover : + public RecursiveASTVisitor<RetainReleaseDeallocRemover> { + Stmt *Body; + MigrationPass &Pass; + + ExprSet Removables; + llvm::OwningPtr<ParentMap> StmtMap; + + Selector DelegateSel; + +public: + RetainReleaseDeallocRemover(MigrationPass &pass) + : Body(0), Pass(pass) { + DelegateSel = + Pass.Ctx.Selectors.getNullarySelector(&Pass.Ctx.Idents.get("delegate")); + } + + void transformBody(Stmt *body) { + Body = body; + collectRemovables(body, Removables); + StmtMap.reset(new ParentMap(body)); + TraverseStmt(body); + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + switch (E->getMethodFamily()) { + default: + return true; + case OMF_autorelease: + if (isRemovable(E)) { + // An unused autorelease is badness. If we remove it the receiver + // will likely die immediately while previously it was kept alive + // by the autorelease pool. This is bad practice in general, leave it + // and emit an error to force the user to restructure his code. + Pass.TA.reportError("it is not safe to remove an unused 'autorelease' " + "message; its receiver may be destroyed immediately", + E->getLocStart(), E->getSourceRange()); + return true; + } + // Pass through. + case OMF_retain: + case OMF_release: + if (E->getReceiverKind() == ObjCMessageExpr::Instance) + if (Expr *rec = E->getInstanceReceiver()) { + rec = rec->IgnoreParenImpCasts(); + if (rec->getType().getObjCLifetime() == Qualifiers::OCL_ExplicitNone && + (E->getMethodFamily() != OMF_retain || isRemovable(E))) { + std::string err = "it is not safe to remove '"; + err += E->getSelector().getAsString() + "' message on " + "an __unsafe_unretained type"; + Pass.TA.reportError(err, rec->getLocStart()); + return true; + } + + if (isGlobalVar(rec) && + (E->getMethodFamily() != OMF_retain || isRemovable(E))) { + std::string err = "it is not safe to remove '"; + err += E->getSelector().getAsString() + "' message on " + "a global variable"; + Pass.TA.reportError(err, rec->getLocStart()); + return true; + } + + if (E->getMethodFamily() == OMF_release && isDelegateMessage(rec)) { + Pass.TA.reportError("it is not safe to remove 'retain' " + "message on the result of a 'delegate' message; " + "the object that was passed to 'setDelegate:' may not be " + "properly retained", rec->getLocStart()); + return true; + } + } + case OMF_dealloc: + break; + } + + switch (E->getReceiverKind()) { + default: + return true; + case ObjCMessageExpr::SuperInstance: { + Transaction Trans(Pass.TA); + clearDiagnostics(E->getSuperLoc()); + if (tryRemoving(E)) + return true; + Pass.TA.replace(E->getSourceRange(), "self"); + return true; + } + case ObjCMessageExpr::Instance: + break; + } + + Expr *rec = E->getInstanceReceiver(); + if (!rec) return true; + + Transaction Trans(Pass.TA); + clearDiagnostics(rec->getExprLoc()); + + if (E->getMethodFamily() == OMF_release && + isRemovable(E) && isInAtFinally(E)) { + // Change the -release to "receiver = nil" in a finally to avoid a leak + // when an exception is thrown. + Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + if (Pass.Ctx.Idents.get("nil").hasMacroDefinition()) + Pass.TA.insertAfterToken(rec->getLocEnd(), " = nil"); + else + Pass.TA.insertAfterToken(rec->getLocEnd(), " = 0"); + return true; + } + + if (!hasSideEffects(E, Pass.Ctx)) { + if (tryRemoving(E)) + return true; + } + Pass.TA.replace(E->getSourceRange(), rec->getSourceRange()); + + return true; + } + +private: + void clearDiagnostics(SourceLocation loc) const { + Pass.TA.clearDiagnostic(diag::err_arc_illegal_explicit_message, + diag::err_unavailable, + diag::err_unavailable_message, + loc); + } + + bool isDelegateMessage(Expr *E) const { + if (!E) return false; + + E = E->IgnoreParenCasts(); + if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) + return (ME->isInstanceMessage() && ME->getSelector() == DelegateSel); + + if (ObjCPropertyRefExpr *propE = dyn_cast<ObjCPropertyRefExpr>(E)) + return propE->getGetterSelector() == DelegateSel; + + return false; + } + + bool isInAtFinally(Expr *E) const { + assert(E); + Stmt *S = E; + while (S) { + if (isa<ObjCAtFinallyStmt>(S)) + return true; + S = StmtMap->getParent(S); + } + + return false; + } + + bool isRemovable(Expr *E) const { + return Removables.count(E); + } + + bool tryRemoving(Expr *E) const { + if (isRemovable(E)) { + Pass.TA.removeStmt(E); + return true; + } + + Stmt *parent = StmtMap->getParent(E); + + if (ImplicitCastExpr *castE = dyn_cast_or_null<ImplicitCastExpr>(parent)) + return tryRemoving(castE); + + if (ParenExpr *parenE = dyn_cast_or_null<ParenExpr>(parent)) + return tryRemoving(parenE); + + if (BinaryOperator * + bopE = dyn_cast_or_null<BinaryOperator>(parent)) { + if (bopE->getOpcode() == BO_Comma && bopE->getLHS() == E && + isRemovable(bopE)) { + Pass.TA.replace(bopE->getSourceRange(), bopE->getRHS()->getSourceRange()); + return true; + } + } + + return false; + } + +}; + +} // anonymous namespace + +void trans::removeRetainReleaseDealloc(MigrationPass &pass) { + BodyTransform<RetainReleaseDeallocRemover> trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransUnbridgedCasts.cpp b/lib/ARCMigrate/TransUnbridgedCasts.cpp new file mode 100644 index 000000000000..1cacd6d84e6d --- /dev/null +++ b/lib/ARCMigrate/TransUnbridgedCasts.cpp @@ -0,0 +1,203 @@ +//===--- TransUnbridgedCasts.cpp - Tranformations to ARC mode -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// rewriteUnbridgedCasts: +// +// A cast of non-objc pointer to an objc one is checked. If the non-objc pointer +// is from a file-level variable, __bridge cast is used to convert it. +// For the result of a function call that we know is +1/+0, +// __bridge/__bridge_transfer is used. +// +// NSString *str = (NSString *)kUTTypePlainText; +// str = b ? kUTTypeRTF : kUTTypePlainText; +// NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, +// _uuid); +// ----> +// NSString *str = (__bridge NSString *)kUTTypePlainText; +// str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText); +// NSString *_uuidString = (__bridge_transfer NSString *) +// CFUUIDCreateString(kCFAllocatorDefault, _uuid); +// +// For a C pointer to ObjC, for casting 'self', __bridge is used. +// +// CFStringRef str = (CFStringRef)self; +// ----> +// CFStringRef str = (__bridge CFStringRef)self; +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class UnbridgedCastRewriter : public RecursiveASTVisitor<UnbridgedCastRewriter>{ + MigrationPass &Pass; + IdentifierInfo *SelfII; +public: + UnbridgedCastRewriter(MigrationPass &pass) : Pass(pass) { + SelfII = &Pass.Ctx.Idents.get("self"); + } + + bool VisitCastExpr(CastExpr *E) { + if (E->getCastKind() != CK_AnyPointerToObjCPointerCast + && E->getCastKind() != CK_BitCast) + return true; + + QualType castType = E->getType(); + Expr *castExpr = E->getSubExpr(); + QualType castExprType = castExpr->getType(); + + if (castType->isObjCObjectPointerType() && + castExprType->isObjCObjectPointerType()) + return true; + if (!castType->isObjCObjectPointerType() && + !castExprType->isObjCObjectPointerType()) + return true; + + bool exprRetainable = castExprType->isObjCIndirectLifetimeType(); + bool castRetainable = castType->isObjCIndirectLifetimeType(); + if (exprRetainable == castRetainable) return true; + + if (castExpr->isNullPointerConstant(Pass.Ctx, + Expr::NPC_ValueDependentIsNull)) + return true; + + SourceLocation loc = castExpr->getExprLoc(); + if (loc.isValid() && Pass.Ctx.getSourceManager().isInSystemHeader(loc)) + return true; + + if (castType->isObjCObjectPointerType()) + transformNonObjCToObjCCast(E); + else + transformObjCToNonObjCCast(E); + + return true; + } + +private: + void transformNonObjCToObjCCast(CastExpr *E) { + if (!E) return; + + // Global vars are assumed that are cast as unretained. + if (isGlobalVar(E)) + if (E->getSubExpr()->getType()->isPointerType()) { + castToObjCObject(E, /*retained=*/false); + return; + } + + // If the cast is directly over the result of a Core Foundation function + // try to figure out whether it should be cast as retained or unretained. + Expr *inner = E->IgnoreParenCasts(); + if (CallExpr *callE = dyn_cast<CallExpr>(inner)) { + if (FunctionDecl *FD = callE->getDirectCallee()) { + if (FD->getAttr<CFReturnsRetainedAttr>()) { + castToObjCObject(E, /*retained=*/true); + return; + } + if (FD->getAttr<CFReturnsNotRetainedAttr>()) { + castToObjCObject(E, /*retained=*/false); + return; + } + if (FD->isGlobal() && + FD->getIdentifier() && + ento::cocoa::isRefType(E->getSubExpr()->getType(), "CF", + FD->getIdentifier()->getName())) { + StringRef fname = FD->getIdentifier()->getName(); + if (fname.endswith("Retain") || + fname.find("Create") != StringRef::npos || + fname.find("Copy") != StringRef::npos) { + castToObjCObject(E, /*retained=*/true); + return; + } + + if (fname.find("Get") != StringRef::npos) { + castToObjCObject(E, /*retained=*/false); + return; + } + } + } + } + } + + void castToObjCObject(CastExpr *E, bool retained) { + rewriteToBridgedCast(E, retained ? OBC_BridgeTransfer : OBC_Bridge); + } + + void rewriteToBridgedCast(CastExpr *E, ObjCBridgeCastKind Kind) { + TransformActions &TA = Pass.TA; + + // We will remove the compiler diagnostic. + if (!TA.hasDiagnostic(diag::err_arc_mismatched_cast, + diag::err_arc_cast_requires_bridge, + E->getLocStart())) + return; + + StringRef bridge; + switch(Kind) { + case OBC_Bridge: + bridge = "__bridge "; break; + case OBC_BridgeTransfer: + bridge = "__bridge_transfer "; break; + case OBC_BridgeRetained: + bridge = "__bridge_retained "; break; + } + + Transaction Trans(TA); + TA.clearDiagnostic(diag::err_arc_mismatched_cast, + diag::err_arc_cast_requires_bridge, + E->getLocStart()); + if (CStyleCastExpr *CCE = dyn_cast<CStyleCastExpr>(E)) { + TA.insertAfterToken(CCE->getLParenLoc(), bridge); + } else { + SourceLocation insertLoc = E->getSubExpr()->getLocStart(); + llvm::SmallString<128> newCast; + newCast += '('; + newCast += bridge; + newCast += E->getType().getAsString(Pass.Ctx.PrintingPolicy); + newCast += ')'; + + if (isa<ParenExpr>(E->getSubExpr())) { + TA.insert(insertLoc, newCast.str()); + } else { + newCast += '('; + TA.insert(insertLoc, newCast.str()); + TA.insertAfterToken(E->getLocEnd(), ")"); + } + } + } + + void transformObjCToNonObjCCast(CastExpr *E) { + if (isSelf(E->getSubExpr())) + return rewriteToBridgedCast(E, OBC_Bridge); + } + + bool isSelf(Expr *E) { + E = E->IgnoreParenLValueCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + if (DRE->getDecl()->getIdentifier() == SelfII) + return true; + return false; + } +}; + +} // end anonymous namespace + +void trans::rewriteUnbridgedCasts(MigrationPass &pass) { + UnbridgedCastRewriter trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransUnusedInitDelegate.cpp b/lib/ARCMigrate/TransUnusedInitDelegate.cpp new file mode 100644 index 000000000000..1019ab4ff1f6 --- /dev/null +++ b/lib/ARCMigrate/TransUnusedInitDelegate.cpp @@ -0,0 +1,74 @@ +//===--- TransUnusedInitDelegate.cpp - Tranformations to ARC mode ---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// Transformations: +//===----------------------------------------------------------------------===// +// +// rewriteUnusedInitDelegate: +// +// Rewrites an unused result of calling a delegate initialization, to assigning +// the result to self. +// e.g +// [self init]; +// ----> +// self = [self init]; +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class UnusedInitRewriter : public RecursiveASTVisitor<UnusedInitRewriter> { + Stmt *Body; + MigrationPass &Pass; + + ExprSet Removables; + +public: + UnusedInitRewriter(MigrationPass &pass) + : Body(0), Pass(pass) { } + + void transformBody(Stmt *body) { + Body = body; + collectRemovables(body, Removables); + TraverseStmt(body); + } + + bool VisitObjCMessageExpr(ObjCMessageExpr *ME) { + if (ME->isDelegateInitCall() && + isRemovable(ME) && + Pass.TA.hasDiagnostic(diag::err_arc_unused_init_message, + ME->getExprLoc())) { + Transaction Trans(Pass.TA); + Pass.TA.clearDiagnostic(diag::err_arc_unused_init_message, + ME->getExprLoc()); + Pass.TA.insert(ME->getExprLoc(), "self = "); + } + return true; + } + +private: + bool isRemovable(Expr *E) const { + return Removables.count(E); + } +}; + +} // anonymous namespace + +void trans::rewriteUnusedInitDelegate(MigrationPass &pass) { + BodyTransform<UnusedInitRewriter> trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp b/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp new file mode 100644 index 000000000000..07ccf70d4dfb --- /dev/null +++ b/lib/ARCMigrate/TransZeroOutPropsInDealloc.cpp @@ -0,0 +1,198 @@ +//===--- TransZeroOutPropsInDealloc.cpp - Tranformations to ARC mode ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// removeZeroOutPropsInDealloc: +// +// Removes zero'ing out "strong" @synthesized properties in a -dealloc method. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +namespace { + +class ZeroOutInDeallocRemover : + public RecursiveASTVisitor<ZeroOutInDeallocRemover> { + typedef RecursiveASTVisitor<ZeroOutInDeallocRemover> base; + + MigrationPass &Pass; + + llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*> SynthesizedProperties; + ImplicitParamDecl *SelfD; + ExprSet Removables; + +public: + ZeroOutInDeallocRemover(MigrationPass &pass) : Pass(pass), SelfD(0) { } + + bool VisitObjCMessageExpr(ObjCMessageExpr *ME) { + ASTContext &Ctx = Pass.Ctx; + TransformActions &TA = Pass.TA; + + if (ME->getReceiverKind() != ObjCMessageExpr::Instance) + return true; + Expr *receiver = ME->getInstanceReceiver(); + if (!receiver) + return true; + + DeclRefExpr *refE = dyn_cast<DeclRefExpr>(receiver->IgnoreParenCasts()); + if (!refE || refE->getDecl() != SelfD) + return true; + + bool BackedBySynthesizeSetter = false; + for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator + P = SynthesizedProperties.begin(), + E = SynthesizedProperties.end(); P != E; ++P) { + ObjCPropertyDecl *PropDecl = P->first; + if (PropDecl->getSetterName() == ME->getSelector()) { + BackedBySynthesizeSetter = true; + break; + } + } + if (!BackedBySynthesizeSetter) + return true; + + // Remove the setter message if RHS is null + Transaction Trans(TA); + Expr *RHS = ME->getArg(0); + bool RHSIsNull = + RHS->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull); + if (RHSIsNull && isRemovable(ME)) + TA.removeStmt(ME); + + return true; + } + + bool VisitBinaryOperator(BinaryOperator *BOE) { + if (isZeroingPropIvar(BOE) && isRemovable(BOE)) { + Transaction Trans(Pass.TA); + Pass.TA.removeStmt(BOE); + } + + return true; + } + + bool TraverseObjCMethodDecl(ObjCMethodDecl *D) { + if (D->getMethodFamily() != OMF_dealloc) + return true; + if (!D->hasBody()) + return true; + + ObjCImplDecl *IMD = dyn_cast<ObjCImplDecl>(D->getDeclContext()); + if (!IMD) + return true; + + SelfD = D->getSelfDecl(); + collectRemovables(D->getBody(), Removables); + + // For a 'dealloc' method use, find all property implementations in + // this class implementation. + for (ObjCImplDecl::propimpl_iterator + I = IMD->propimpl_begin(), EI = IMD->propimpl_end(); I != EI; ++I) { + ObjCPropertyImplDecl *PID = *I; + if (PID->getPropertyImplementation() == + ObjCPropertyImplDecl::Synthesize) { + ObjCPropertyDecl *PD = PID->getPropertyDecl(); + ObjCMethodDecl *setterM = PD->getSetterMethodDecl(); + if (!(setterM && setterM->isDefined())) { + ObjCPropertyDecl::PropertyAttributeKind AttrKind = + PD->getPropertyAttributes(); + if (AttrKind & + (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy | + ObjCPropertyDecl::OBJC_PR_strong)) + SynthesizedProperties[PD] = PID; + } + } + } + + // Now, remove all zeroing of ivars etc. + base::TraverseObjCMethodDecl(D); + + // clear out for next method. + SynthesizedProperties.clear(); + SelfD = 0; + Removables.clear(); + return true; + } + + bool TraverseFunctionDecl(FunctionDecl *D) { return true; } + bool TraverseBlockDecl(BlockDecl *block) { return true; } + bool TraverseBlockExpr(BlockExpr *block) { return true; } + +private: + bool isRemovable(Expr *E) const { + return Removables.count(E); + } + + bool isZeroingPropIvar(Expr *E) { + BinaryOperator *BOE = dyn_cast_or_null<BinaryOperator>(E); + if (!BOE) return false; + + if (BOE->getOpcode() == BO_Comma) + return isZeroingPropIvar(BOE->getLHS()) && + isZeroingPropIvar(BOE->getRHS()); + + if (BOE->getOpcode() != BO_Assign) + return false; + + ASTContext &Ctx = Pass.Ctx; + + Expr *LHS = BOE->getLHS(); + if (ObjCIvarRefExpr *IV = dyn_cast<ObjCIvarRefExpr>(LHS)) { + ObjCIvarDecl *IVDecl = IV->getDecl(); + if (!IVDecl->getType()->isObjCObjectPointerType()) + return false; + bool IvarBacksPropertySynthesis = false; + for (llvm::DenseMap<ObjCPropertyDecl*, ObjCPropertyImplDecl*>::iterator + P = SynthesizedProperties.begin(), + E = SynthesizedProperties.end(); P != E; ++P) { + ObjCPropertyImplDecl *PropImpDecl = P->second; + if (PropImpDecl && PropImpDecl->getPropertyIvarDecl() == IVDecl) { + IvarBacksPropertySynthesis = true; + break; + } + } + if (!IvarBacksPropertySynthesis) + return false; + } + else if (ObjCPropertyRefExpr *PropRefExp = dyn_cast<ObjCPropertyRefExpr>(LHS)) { + // TODO: Using implicit property decl. + if (PropRefExp->isImplicitProperty()) + return false; + if (ObjCPropertyDecl *PDecl = PropRefExp->getExplicitProperty()) { + if (!SynthesizedProperties.count(PDecl)) + return false; + } + } + else + return false; + + Expr *RHS = BOE->getRHS(); + bool RHSIsNull = RHS->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull); + if (RHSIsNull) + return true; + + return isZeroingPropIvar(RHS); + } +}; + +} // anonymous namespace + +void trans::removeZeroOutPropsInDealloc(MigrationPass &pass) { + ZeroOutInDeallocRemover trans(pass); + trans.TraverseDecl(pass.Ctx.getTranslationUnitDecl()); +} diff --git a/lib/ARCMigrate/TransformActions.cpp b/lib/ARCMigrate/TransformActions.cpp new file mode 100644 index 000000000000..c99940b494b9 --- /dev/null +++ b/lib/ARCMigrate/TransformActions.cpp @@ -0,0 +1,699 @@ +//===--- ARCMT.cpp - Migration to ARC mode --------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Internals.h" +#include "clang/AST/Expr.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/DenseSet.h" +#include <map> + +using namespace clang; +using namespace arcmt; +using llvm::StringRef; + +namespace { + +/// \brief Collects transformations and merges them before applying them with +/// with applyRewrites(). E.g. if the same source range +/// is requested to be removed twice, only one rewriter remove will be invoked. +/// Rewrites happen in "transactions"; if one rewrite in the transaction cannot +/// be done (e.g. it resides in a macro) all rewrites in the transaction are +/// aborted. +/// FIXME: "Transactional" rewrites support should be baked in the Rewriter. +class TransformActionsImpl { + CapturedDiagList &CapturedDiags; + ASTContext &Ctx; + Preprocessor &PP; + + bool IsInTransaction; + + enum ActionKind { + Act_Insert, Act_InsertAfterToken, + Act_Remove, Act_RemoveStmt, + Act_Replace, Act_ReplaceText, + Act_IncreaseIndentation, + Act_ClearDiagnostic + }; + + struct ActionData { + ActionKind Kind; + SourceLocation Loc; + SourceRange R1, R2; + llvm::StringRef Text1, Text2; + Stmt *S; + llvm::SmallVector<unsigned, 2> DiagIDs; + }; + + std::vector<ActionData> CachedActions; + + enum RangeComparison { + Range_Before, + Range_After, + Range_Contains, + Range_Contained, + Range_ExtendsBegin, + Range_ExtendsEnd + }; + + /// \brief A range to remove. It is a character range. + struct CharRange { + FullSourceLoc Begin, End; + + CharRange(CharSourceRange range, SourceManager &srcMgr, Preprocessor &PP) { + SourceLocation beginLoc = range.getBegin(), endLoc = range.getEnd(); + assert(beginLoc.isValid() && endLoc.isValid()); + if (range.isTokenRange()) { + Begin = FullSourceLoc(srcMgr.getInstantiationLoc(beginLoc), srcMgr); + End = FullSourceLoc(getLocForEndOfToken(endLoc, srcMgr, PP), srcMgr); + } else { + Begin = FullSourceLoc(srcMgr.getInstantiationLoc(beginLoc), srcMgr); + End = FullSourceLoc(srcMgr.getInstantiationLoc(endLoc), srcMgr); + } + assert(Begin.isValid() && End.isValid()); + } + + RangeComparison compareWith(const CharRange &RHS) const { + if (End.isBeforeInTranslationUnitThan(RHS.Begin)) + return Range_Before; + if (RHS.End.isBeforeInTranslationUnitThan(Begin)) + return Range_After; + if (!Begin.isBeforeInTranslationUnitThan(RHS.Begin) && + !RHS.End.isBeforeInTranslationUnitThan(End)) + return Range_Contained; + if (Begin.isBeforeInTranslationUnitThan(RHS.Begin) && + RHS.End.isBeforeInTranslationUnitThan(End)) + return Range_Contains; + if (Begin.isBeforeInTranslationUnitThan(RHS.Begin)) + return Range_ExtendsBegin; + else + return Range_ExtendsEnd; + } + + static RangeComparison compare(SourceRange LHS, SourceRange RHS, + SourceManager &SrcMgr, Preprocessor &PP) { + return CharRange(CharSourceRange::getTokenRange(LHS), SrcMgr, PP) + .compareWith(CharRange(CharSourceRange::getTokenRange(RHS), + SrcMgr, PP)); + } + }; + + typedef llvm::SmallVector<StringRef, 2> TextsVec; + typedef std::map<FullSourceLoc, TextsVec, FullSourceLoc::BeforeThanCompare> + InsertsMap; + InsertsMap Inserts; + /// \brief A list of ranges to remove. They are always sorted and they never + /// intersect with each other. + std::list<CharRange> Removals; + + llvm::DenseSet<Stmt *> StmtRemovals; + + std::vector<std::pair<CharRange, SourceLocation> > IndentationRanges; + + /// \brief Keeps text passed to transformation methods. + llvm::StringMap<bool> UniqueText; + +public: + TransformActionsImpl(CapturedDiagList &capturedDiags, + ASTContext &ctx, Preprocessor &PP) + : CapturedDiags(capturedDiags), Ctx(ctx), PP(PP), IsInTransaction(false) { } + + void startTransaction(); + bool commitTransaction(); + void abortTransaction(); + + bool isInTransaction() const { return IsInTransaction; } + + void insert(SourceLocation loc, llvm::StringRef text); + void insertAfterToken(SourceLocation loc, llvm::StringRef text); + void remove(SourceRange range); + void removeStmt(Stmt *S); + void replace(SourceRange range, llvm::StringRef text); + void replace(SourceRange range, SourceRange replacementRange); + void replaceStmt(Stmt *S, llvm::StringRef text); + void replaceText(SourceLocation loc, llvm::StringRef text, + llvm::StringRef replacementText); + void increaseIndentation(SourceRange range, + SourceLocation parentIndent); + + bool clearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range); + + void applyRewrites(TransformActions::RewriteReceiver &receiver); + +private: + bool canInsert(SourceLocation loc); + bool canInsertAfterToken(SourceLocation loc); + bool canRemoveRange(SourceRange range); + bool canReplaceRange(SourceRange range, SourceRange replacementRange); + bool canReplaceText(SourceLocation loc, llvm::StringRef text); + + void commitInsert(SourceLocation loc, StringRef text); + void commitInsertAfterToken(SourceLocation loc, StringRef text); + void commitRemove(SourceRange range); + void commitRemoveStmt(Stmt *S); + void commitReplace(SourceRange range, SourceRange replacementRange); + void commitReplaceText(SourceLocation loc, llvm::StringRef text, + llvm::StringRef replacementText); + void commitIncreaseIndentation(SourceRange range,SourceLocation parentIndent); + void commitClearDiagnostic(llvm::ArrayRef<unsigned> IDs, SourceRange range); + + void addRemoval(CharSourceRange range); + void addInsertion(SourceLocation loc, StringRef text); + + /// \brief Stores text passed to the transformation methods to keep the string + /// "alive". Since the vast majority of text will be the same, we also unique + /// the strings using a StringMap. + StringRef getUniqueText(StringRef text); + + /// \brief Computes the source location just past the end of the token at + /// the given source location. If the location points at a macro, the whole + /// macro expansion is skipped. + static SourceLocation getLocForEndOfToken(SourceLocation loc, + SourceManager &SM,Preprocessor &PP); +}; + +} // anonymous namespace + +void TransformActionsImpl::startTransaction() { + assert(!IsInTransaction && + "Cannot start a transaction in the middle of another one"); + IsInTransaction = true; +} + +bool TransformActionsImpl::commitTransaction() { + assert(IsInTransaction && "No transaction started"); + + if (CachedActions.empty()) { + IsInTransaction = false; + return false; + } + + // Verify that all actions are possible otherwise abort the whole transaction. + bool AllActionsPossible = true; + for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) { + ActionData &act = CachedActions[i]; + switch (act.Kind) { + case Act_Insert: + if (!canInsert(act.Loc)) + AllActionsPossible = false; + break; + case Act_InsertAfterToken: + if (!canInsertAfterToken(act.Loc)) + AllActionsPossible = false; + break; + case Act_Remove: + if (!canRemoveRange(act.R1)) + AllActionsPossible = false; + break; + case Act_RemoveStmt: + assert(act.S); + if (!canRemoveRange(act.S->getSourceRange())) + AllActionsPossible = false; + break; + case Act_Replace: + if (!canReplaceRange(act.R1, act.R2)) + AllActionsPossible = false; + break; + case Act_ReplaceText: + if (!canReplaceText(act.Loc, act.Text1)) + AllActionsPossible = false; + break; + case Act_IncreaseIndentation: + // This is not important, we don't care if it will fail. + break; + case Act_ClearDiagnostic: + // We are just checking source rewrites. + break; + } + if (!AllActionsPossible) + break; + } + + if (!AllActionsPossible) { + abortTransaction(); + return true; + } + + for (unsigned i = 0, e = CachedActions.size(); i != e; ++i) { + ActionData &act = CachedActions[i]; + switch (act.Kind) { + case Act_Insert: + commitInsert(act.Loc, act.Text1); + break; + case Act_InsertAfterToken: + commitInsertAfterToken(act.Loc, act.Text1); + break; + case Act_Remove: + commitRemove(act.R1); + break; + case Act_RemoveStmt: + commitRemoveStmt(act.S); + break; + case Act_Replace: + commitReplace(act.R1, act.R2); + break; + case Act_ReplaceText: + commitReplaceText(act.Loc, act.Text1, act.Text2); + break; + case Act_IncreaseIndentation: + commitIncreaseIndentation(act.R1, act.Loc); + break; + case Act_ClearDiagnostic: + commitClearDiagnostic(act.DiagIDs, act.R1); + break; + } + } + + CachedActions.clear(); + IsInTransaction = false; + return false; +} + +void TransformActionsImpl::abortTransaction() { + assert(IsInTransaction && "No transaction started"); + CachedActions.clear(); + IsInTransaction = false; +} + +void TransformActionsImpl::insert(SourceLocation loc, StringRef text) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + text = getUniqueText(text); + ActionData data; + data.Kind = Act_Insert; + data.Loc = loc; + data.Text1 = text; + CachedActions.push_back(data); +} + +void TransformActionsImpl::insertAfterToken(SourceLocation loc, StringRef text) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + text = getUniqueText(text); + ActionData data; + data.Kind = Act_InsertAfterToken; + data.Loc = loc; + data.Text1 = text; + CachedActions.push_back(data); +} + +void TransformActionsImpl::remove(SourceRange range) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + ActionData data; + data.Kind = Act_Remove; + data.R1 = range; + CachedActions.push_back(data); +} + +void TransformActionsImpl::removeStmt(Stmt *S) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + ActionData data; + data.Kind = Act_RemoveStmt; + data.S = S->IgnoreImplicit(); // important for uniquing + CachedActions.push_back(data); +} + +void TransformActionsImpl::replace(SourceRange range, StringRef text) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + text = getUniqueText(text); + remove(range); + insert(range.getBegin(), text); +} + +void TransformActionsImpl::replace(SourceRange range, + SourceRange replacementRange) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + ActionData data; + data.Kind = Act_Replace; + data.R1 = range; + data.R2 = replacementRange; + CachedActions.push_back(data); +} + +void TransformActionsImpl::replaceText(SourceLocation loc, StringRef text, + StringRef replacementText) { + text = getUniqueText(text); + replacementText = getUniqueText(replacementText); + ActionData data; + data.Kind = Act_ReplaceText; + data.Loc = loc; + data.Text1 = text; + data.Text2 = replacementText; + CachedActions.push_back(data); +} + +void TransformActionsImpl::replaceStmt(Stmt *S, StringRef text) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + text = getUniqueText(text); + insert(S->getLocStart(), text); + removeStmt(S); +} + +void TransformActionsImpl::increaseIndentation(SourceRange range, + SourceLocation parentIndent) { + if (range.isInvalid()) return; + assert(IsInTransaction && "Actions only allowed during a transaction"); + ActionData data; + data.Kind = Act_IncreaseIndentation; + data.R1 = range; + data.Loc = parentIndent; + CachedActions.push_back(data); +} + +bool TransformActionsImpl::clearDiagnostic(llvm::ArrayRef<unsigned> IDs, + SourceRange range) { + assert(IsInTransaction && "Actions only allowed during a transaction"); + if (!CapturedDiags.hasDiagnostic(IDs, range)) + return false; + + ActionData data; + data.Kind = Act_ClearDiagnostic; + data.R1 = range; + data.DiagIDs.append(IDs.begin(), IDs.end()); + CachedActions.push_back(data); + return true; +} + +bool TransformActionsImpl::canInsert(SourceLocation loc) { + if (loc.isInvalid()) + return false; + + SourceManager &SM = Ctx.getSourceManager(); + if (SM.isInSystemHeader(SM.getInstantiationLoc(loc))) + return false; + + if (loc.isFileID()) + return true; + return PP.isAtStartOfMacroExpansion(loc); +} + +bool TransformActionsImpl::canInsertAfterToken(SourceLocation loc) { + if (loc.isInvalid()) + return false; + + SourceManager &SM = Ctx.getSourceManager(); + if (SM.isInSystemHeader(SM.getInstantiationLoc(loc))) + return false; + + if (loc.isFileID()) + return true; + return PP.isAtEndOfMacroExpansion(loc); +} + +bool TransformActionsImpl::canRemoveRange(SourceRange range) { + return canInsert(range.getBegin()) && canInsertAfterToken(range.getEnd()); +} + +bool TransformActionsImpl::canReplaceRange(SourceRange range, + SourceRange replacementRange) { + return canRemoveRange(range) && canRemoveRange(replacementRange); +} + +bool TransformActionsImpl::canReplaceText(SourceLocation loc, StringRef text) { + if (!canInsert(loc)) + return false; + + SourceManager &SM = Ctx.getSourceManager(); + loc = SM.getInstantiationLoc(loc); + + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); + + // Try to load the file buffer. + bool invalidTemp = false; + llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return false; + + return file.substr(locInfo.second).startswith(text); +} + +void TransformActionsImpl::commitInsert(SourceLocation loc, StringRef text) { + addInsertion(loc, text); +} + +void TransformActionsImpl::commitInsertAfterToken(SourceLocation loc, + StringRef text) { + addInsertion(getLocForEndOfToken(loc, Ctx.getSourceManager(), PP), text); +} + +void TransformActionsImpl::commitRemove(SourceRange range) { + addRemoval(CharSourceRange::getTokenRange(range)); +} + +void TransformActionsImpl::commitRemoveStmt(Stmt *S) { + assert(S); + if (StmtRemovals.count(S)) + return; // already removed. + + if (Expr *E = dyn_cast<Expr>(S)) { + commitRemove(E->getSourceRange()); + commitInsert(E->getSourceRange().getBegin(), getARCMTMacroName()); + } else + commitRemove(S->getSourceRange()); + + StmtRemovals.insert(S); +} + +void TransformActionsImpl::commitReplace(SourceRange range, + SourceRange replacementRange) { + RangeComparison comp = CharRange::compare(replacementRange, range, + Ctx.getSourceManager(), PP); + assert(comp == Range_Contained); + if (comp != Range_Contained) + return; // Although we asserted, be extra safe for release build. + if (range.getBegin() != replacementRange.getBegin()) + addRemoval(CharSourceRange::getCharRange(range.getBegin(), + replacementRange.getBegin())); + if (replacementRange.getEnd() != range.getEnd()) + addRemoval(CharSourceRange::getTokenRange( + getLocForEndOfToken(replacementRange.getEnd(), + Ctx.getSourceManager(), PP), + range.getEnd())); +} +void TransformActionsImpl::commitReplaceText(SourceLocation loc, + StringRef text, + StringRef replacementText) { + SourceManager &SM = Ctx.getSourceManager(); + loc = SM.getInstantiationLoc(loc); + // canReplaceText already checked if loc points at text. + SourceLocation afterText = loc.getFileLocWithOffset(text.size()); + + addRemoval(CharSourceRange::getCharRange(loc, afterText)); + commitInsert(loc, replacementText); +} + +void TransformActionsImpl::commitIncreaseIndentation(SourceRange range, + SourceLocation parentIndent) { + SourceManager &SM = Ctx.getSourceManager(); + IndentationRanges.push_back( + std::make_pair(CharRange(CharSourceRange::getTokenRange(range), + SM, PP), + SM.getInstantiationLoc(parentIndent))); +} + +void TransformActionsImpl::commitClearDiagnostic(llvm::ArrayRef<unsigned> IDs, + SourceRange range) { + CapturedDiags.clearDiagnostic(IDs, range); +} + +void TransformActionsImpl::addInsertion(SourceLocation loc, StringRef text) { + SourceManager &SM = Ctx.getSourceManager(); + loc = SM.getInstantiationLoc(loc); + for (std::list<CharRange>::reverse_iterator + I = Removals.rbegin(), E = Removals.rend(); I != E; ++I) { + if (!SM.isBeforeInTranslationUnit(loc, I->End)) + break; + if (I->Begin.isBeforeInTranslationUnitThan(loc)) + return; + } + + Inserts[FullSourceLoc(loc, SM)].push_back(text); +} + +void TransformActionsImpl::addRemoval(CharSourceRange range) { + CharRange newRange(range, Ctx.getSourceManager(), PP); + if (newRange.Begin == newRange.End) + return; + + Inserts.erase(Inserts.upper_bound(newRange.Begin), + Inserts.lower_bound(newRange.End)); + + std::list<CharRange>::iterator I = Removals.end(); + while (I != Removals.begin()) { + std::list<CharRange>::iterator RI = I; + --RI; + RangeComparison comp = newRange.compareWith(*RI); + switch (comp) { + case Range_Before: + --I; + break; + case Range_After: + Removals.insert(I, newRange); + return; + case Range_Contained: + return; + case Range_Contains: + RI->End = newRange.End; + case Range_ExtendsBegin: + newRange.End = RI->End; + Removals.erase(RI); + break; + case Range_ExtendsEnd: + RI->End = newRange.End; + return; + } + } + + Removals.insert(Removals.begin(), newRange); +} + +void TransformActionsImpl::applyRewrites( + TransformActions::RewriteReceiver &receiver) { + for (InsertsMap::iterator I = Inserts.begin(), E = Inserts.end(); I!=E; ++I) { + SourceLocation loc = I->first; + for (TextsVec::iterator + TI = I->second.begin(), TE = I->second.end(); TI != TE; ++TI) { + receiver.insert(loc, *TI); + } + } + + for (std::vector<std::pair<CharRange, SourceLocation> >::iterator + I = IndentationRanges.begin(), E = IndentationRanges.end(); I!=E; ++I) { + CharSourceRange range = CharSourceRange::getCharRange(I->first.Begin, + I->first.End); + receiver.increaseIndentation(range, I->second); + } + + for (std::list<CharRange>::iterator + I = Removals.begin(), E = Removals.end(); I != E; ++I) { + CharSourceRange range = CharSourceRange::getCharRange(I->Begin, I->End); + receiver.remove(range); + } +} + +/// \brief Stores text passed to the transformation methods to keep the string +/// "alive". Since the vast majority of text will be the same, we also unique +/// the strings using a StringMap. +StringRef TransformActionsImpl::getUniqueText(StringRef text) { + llvm::StringMapEntry<bool> &entry = UniqueText.GetOrCreateValue(text); + return entry.getKey(); +} + +/// \brief Computes the source location just past the end of the token at +/// the given source location. If the location points at a macro, the whole +/// macro expansion is skipped. +SourceLocation TransformActionsImpl::getLocForEndOfToken(SourceLocation loc, + SourceManager &SM, + Preprocessor &PP) { + if (loc.isMacroID()) + loc = SM.getInstantiationRange(loc).second; + return PP.getLocForEndOfToken(loc); +} + +TransformActions::RewriteReceiver::~RewriteReceiver() { } + +TransformActions::TransformActions(Diagnostic &diag, + CapturedDiagList &capturedDiags, + ASTContext &ctx, Preprocessor &PP) + : Diags(diag), CapturedDiags(capturedDiags) { + Impl = new TransformActionsImpl(capturedDiags, ctx, PP); +} + +TransformActions::~TransformActions() { + delete static_cast<TransformActionsImpl*>(Impl); +} + +void TransformActions::startTransaction() { + static_cast<TransformActionsImpl*>(Impl)->startTransaction(); +} + +bool TransformActions::commitTransaction() { + return static_cast<TransformActionsImpl*>(Impl)->commitTransaction(); +} + +void TransformActions::abortTransaction() { + static_cast<TransformActionsImpl*>(Impl)->abortTransaction(); +} + + +void TransformActions::insert(SourceLocation loc, llvm::StringRef text) { + static_cast<TransformActionsImpl*>(Impl)->insert(loc, text); +} + +void TransformActions::insertAfterToken(SourceLocation loc, + llvm::StringRef text) { + static_cast<TransformActionsImpl*>(Impl)->insertAfterToken(loc, text); +} + +void TransformActions::remove(SourceRange range) { + static_cast<TransformActionsImpl*>(Impl)->remove(range); +} + +void TransformActions::removeStmt(Stmt *S) { + static_cast<TransformActionsImpl*>(Impl)->removeStmt(S); +} + +void TransformActions::replace(SourceRange range, llvm::StringRef text) { + static_cast<TransformActionsImpl*>(Impl)->replace(range, text); +} + +void TransformActions::replace(SourceRange range, + SourceRange replacementRange) { + static_cast<TransformActionsImpl*>(Impl)->replace(range, replacementRange); +} + +void TransformActions::replaceStmt(Stmt *S, llvm::StringRef text) { + static_cast<TransformActionsImpl*>(Impl)->replaceStmt(S, text); +} + +void TransformActions::replaceText(SourceLocation loc, llvm::StringRef text, + llvm::StringRef replacementText) { + static_cast<TransformActionsImpl*>(Impl)->replaceText(loc, text, + replacementText); +} + +void TransformActions::increaseIndentation(SourceRange range, + SourceLocation parentIndent) { + static_cast<TransformActionsImpl*>(Impl)->increaseIndentation(range, + parentIndent); +} + +bool TransformActions::clearDiagnostic(llvm::ArrayRef<unsigned> IDs, + SourceRange range) { + return static_cast<TransformActionsImpl*>(Impl)->clearDiagnostic(IDs, range); +} + +void TransformActions::applyRewrites(RewriteReceiver &receiver) { + static_cast<TransformActionsImpl*>(Impl)->applyRewrites(receiver); +} + +void TransformActions::reportError(llvm::StringRef error, SourceLocation loc, + SourceRange range) { + assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() && + "Errors should be emitted out of a transaction"); + // FIXME: Use a custom category name to distinguish rewriter errors. + std::string rewriteErr = "[rewriter] "; + rewriteErr += error; + unsigned diagID + = Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Error, + rewriteErr); + Diags.Report(loc, diagID) << range; +} + +void TransformActions::reportNote(llvm::StringRef note, SourceLocation loc, + SourceRange range) { + assert(!static_cast<TransformActionsImpl*>(Impl)->isInTransaction() && + "Errors should be emitted out of a transaction"); + // FIXME: Use a custom category name to distinguish rewriter errors. + std::string rewriteNote = "[rewriter] "; + rewriteNote += note; + unsigned diagID + = Diags.getDiagnosticIDs()->getCustomDiagID(DiagnosticIDs::Note, + rewriteNote); + Diags.Report(loc, diagID) << range; +} diff --git a/lib/ARCMigrate/Transforms.cpp b/lib/ARCMigrate/Transforms.cpp new file mode 100644 index 000000000000..7bd95e54bc27 --- /dev/null +++ b/lib/ARCMigrate/Transforms.cpp @@ -0,0 +1,296 @@ +//===--- Tranforms.cpp - Tranformations to ARC mode -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Transforms.h" +#include "Internals.h" +#include "clang/Sema/SemaDiagnostic.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Lex/Lexer.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/DenseSet.h" +#include <map> + +using namespace clang; +using namespace arcmt; +using namespace trans; +using llvm::StringRef; + +//===----------------------------------------------------------------------===// +// Helpers. +//===----------------------------------------------------------------------===// + +/// \brief True if the class is one that does not support weak. +static bool isClassInWeakBlacklist(ObjCInterfaceDecl *cls) { + if (!cls) + return false; + + bool inList = llvm::StringSwitch<bool>(cls->getName()) + .Case("NSColorSpace", true) + .Case("NSFont", true) + .Case("NSFontPanel", true) + .Case("NSImage", true) + .Case("NSLazyBrowserCell", true) + .Case("NSWindow", true) + .Case("NSWindowController", true) + .Case("NSMenuView", true) + .Case("NSPersistentUIWindowInfo", true) + .Case("NSTableCellView", true) + .Case("NSATSTypeSetter", true) + .Case("NSATSGlyphStorage", true) + .Case("NSLineFragmentRenderingContext", true) + .Case("NSAttributeDictionary", true) + .Case("NSParagraphStyle", true) + .Case("NSTextTab", true) + .Case("NSSimpleHorizontalTypesetter", true) + .Case("_NSCachedAttributedString", true) + .Case("NSStringDrawingTextStorage", true) + .Case("NSTextView", true) + .Case("NSSubTextStorage", true) + .Default(false); + + if (inList) + return true; + + return isClassInWeakBlacklist(cls->getSuperClass()); +} + +bool trans::canApplyWeak(ASTContext &Ctx, QualType type) { + if (!Ctx.getLangOptions().ObjCRuntimeHasWeak) + return false; + + QualType T = type; + while (const PointerType *ptr = T->getAs<PointerType>()) + T = ptr->getPointeeType(); + if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) { + ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl(); + if (!Class || Class->getName() == "NSObject") + return false; // id/NSObject is not safe for weak. + if (Class->isForwardDecl()) + return false; // forward classes are not verifiable, therefore not safe. + if (Class->isArcWeakrefUnavailable()) + return false; + if (isClassInWeakBlacklist(Class)) + return false; + } + + return true; +} + +/// \brief 'Loc' is the end of a statement range. This returns the location +/// immediately after the semicolon following the statement. +/// If no semicolon is found or the location is inside a macro, the returned +/// source location will be invalid. +SourceLocation trans::findLocationAfterSemi(SourceLocation loc, + ASTContext &Ctx) { + SourceManager &SM = Ctx.getSourceManager(); + if (loc.isMacroID()) { + if (!Lexer::isAtEndOfMacroExpansion(loc, SM, Ctx.getLangOptions())) + return SourceLocation(); + loc = SM.getInstantiationRange(loc).second; + } + loc = Lexer::getLocForEndOfToken(loc, /*Offset=*/0, SM, Ctx.getLangOptions()); + + // Break down the source location. + std::pair<FileID, unsigned> locInfo = SM.getDecomposedLoc(loc); + + // Try to load the file buffer. + bool invalidTemp = false; + llvm::StringRef file = SM.getBufferData(locInfo.first, &invalidTemp); + if (invalidTemp) + return SourceLocation(); + + const char *tokenBegin = file.data() + locInfo.second; + + // Lex from the start of the given location. + Lexer lexer(SM.getLocForStartOfFile(locInfo.first), + Ctx.getLangOptions(), + file.begin(), tokenBegin, file.end()); + Token tok; + lexer.LexFromRawLexer(tok); + if (tok.isNot(tok::semi)) + return SourceLocation(); + + return tok.getLocation().getFileLocWithOffset(1); +} + +bool trans::hasSideEffects(Expr *E, ASTContext &Ctx) { + if (!E || !E->HasSideEffects(Ctx)) + return false; + + E = E->IgnoreParenCasts(); + ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E); + if (!ME) + return true; + switch (ME->getMethodFamily()) { + case OMF_autorelease: + case OMF_dealloc: + case OMF_release: + case OMF_retain: + switch (ME->getReceiverKind()) { + case ObjCMessageExpr::SuperInstance: + return false; + case ObjCMessageExpr::Instance: + return hasSideEffects(ME->getInstanceReceiver(), Ctx); + default: + break; + } + break; + default: + break; + } + + return true; +} + +bool trans::isGlobalVar(Expr *E) { + E = E->IgnoreParenCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getDecl()->getDeclContext()->isFileContext(); + if (ConditionalOperator *condOp = dyn_cast<ConditionalOperator>(E)) + return isGlobalVar(condOp->getTrueExpr()) && + isGlobalVar(condOp->getFalseExpr()); + + return false; +} + +namespace { + +class ReferenceClear : public RecursiveASTVisitor<ReferenceClear> { + ExprSet &Refs; +public: + ReferenceClear(ExprSet &refs) : Refs(refs) { } + bool VisitDeclRefExpr(DeclRefExpr *E) { Refs.erase(E); return true; } + bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { Refs.erase(E); return true; } +}; + +class ReferenceCollector : public RecursiveASTVisitor<ReferenceCollector> { + ValueDecl *Dcl; + ExprSet &Refs; + +public: + ReferenceCollector(ValueDecl *D, ExprSet &refs) + : Dcl(D), Refs(refs) { } + + bool VisitDeclRefExpr(DeclRefExpr *E) { + if (E->getDecl() == Dcl) + Refs.insert(E); + return true; + } + + bool VisitBlockDeclRefExpr(BlockDeclRefExpr *E) { + if (E->getDecl() == Dcl) + Refs.insert(E); + return true; + } +}; + +class RemovablesCollector : public RecursiveASTVisitor<RemovablesCollector> { + ExprSet &Removables; + +public: + RemovablesCollector(ExprSet &removables) + : Removables(removables) { } + + bool shouldWalkTypesOfTypeLocs() const { return false; } + + bool TraverseStmtExpr(StmtExpr *E) { + CompoundStmt *S = E->getSubStmt(); + for (CompoundStmt::body_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) { + if (I != E - 1) + mark(*I); + TraverseStmt(*I); + } + return true; + } + + bool VisitCompoundStmt(CompoundStmt *S) { + for (CompoundStmt::body_iterator + I = S->body_begin(), E = S->body_end(); I != E; ++I) + mark(*I); + return true; + } + + bool VisitIfStmt(IfStmt *S) { + mark(S->getThen()); + mark(S->getElse()); + return true; + } + + bool VisitWhileStmt(WhileStmt *S) { + mark(S->getBody()); + return true; + } + + bool VisitDoStmt(DoStmt *S) { + mark(S->getBody()); + return true; + } + + bool VisitForStmt(ForStmt *S) { + mark(S->getInit()); + mark(S->getInc()); + mark(S->getBody()); + return true; + } + +private: + void mark(Stmt *S) { + if (!S) return; + + while (LabelStmt *Label = dyn_cast<LabelStmt>(S)) + S = Label->getSubStmt(); + S = S->IgnoreImplicit(); + if (Expr *E = dyn_cast<Expr>(S)) + Removables.insert(E); + } +}; + +} // end anonymous namespace + +void trans::clearRefsIn(Stmt *S, ExprSet &refs) { + ReferenceClear(refs).TraverseStmt(S); +} + +void trans::collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs) { + ReferenceCollector(D, refs).TraverseStmt(S); +} + +void trans::collectRemovables(Stmt *S, ExprSet &exprs) { + RemovablesCollector(exprs).TraverseStmt(S); +} + +//===----------------------------------------------------------------------===// +// getAllTransformations. +//===----------------------------------------------------------------------===// + +static void independentTransforms(MigrationPass &pass) { + rewriteAutoreleasePool(pass); + rewriteProperties(pass); + removeRetainReleaseDealloc(pass); + rewriteUnusedInitDelegate(pass); + removeZeroOutPropsInDealloc(pass); + makeAssignARCSafe(pass); + rewriteUnbridgedCasts(pass); + rewriteBlockObjCVariable(pass); +} + +std::vector<TransformFn> arcmt::getAllTransformations() { + std::vector<TransformFn> transforms; + + transforms.push_back(independentTransforms); + // This depends on previous transformations removing various expressions. + transforms.push_back(removeEmptyStatementsAndDealloc); + + return transforms; +} diff --git a/lib/ARCMigrate/Transforms.h b/lib/ARCMigrate/Transforms.h new file mode 100644 index 000000000000..b47d6d8e9b8b --- /dev/null +++ b/lib/ARCMigrate/Transforms.h @@ -0,0 +1,92 @@ +//===-- Transforms.h - Tranformations to ARC mode ---------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H +#define LLVM_CLANG_LIB_ARCMIGRATE_TRANSFORMS_H + +#include "clang/AST/RecursiveASTVisitor.h" +#include "llvm/ADT/DenseSet.h" + +namespace clang { + class Decl; + class Stmt; + class BlockDecl; + class ObjCMethodDecl; + class FunctionDecl; + +namespace arcmt { + class MigrationPass; + +namespace trans { + +//===----------------------------------------------------------------------===// +// Transformations. +//===----------------------------------------------------------------------===// + +void rewriteAutoreleasePool(MigrationPass &pass); +void rewriteUnbridgedCasts(MigrationPass &pass); +void makeAssignARCSafe(MigrationPass &pass); +void removeRetainReleaseDealloc(MigrationPass &pass); +void removeZeroOutPropsInDealloc(MigrationPass &pass); +void rewriteProperties(MigrationPass &pass); +void rewriteBlockObjCVariable(MigrationPass &pass); +void rewriteUnusedInitDelegate(MigrationPass &pass); + +void removeEmptyStatementsAndDealloc(MigrationPass &pass); + +//===----------------------------------------------------------------------===// +// Helpers. +//===----------------------------------------------------------------------===// + +/// \brief Determine whether we can add weak to the given type. +bool canApplyWeak(ASTContext &Ctx, QualType type); + +/// \brief 'Loc' is the end of a statement range. This returns the location +/// immediately after the semicolon following the statement. +/// If no semicolon is found or the location is inside a macro, the returned +/// source location will be invalid. +SourceLocation findLocationAfterSemi(SourceLocation loc, ASTContext &Ctx); + +bool hasSideEffects(Expr *E, ASTContext &Ctx); +bool isGlobalVar(Expr *E); + + +template <typename BODY_TRANS> +class BodyTransform : public RecursiveASTVisitor<BodyTransform<BODY_TRANS> > { + MigrationPass &Pass; + +public: + BodyTransform(MigrationPass &pass) : Pass(pass) { } + + bool TraverseStmt(Stmt *rootS) { + BODY_TRANS(Pass).transformBody(rootS); + return true; + } +}; + +typedef llvm::DenseSet<Expr *> ExprSet; + +void clearRefsIn(Stmt *S, ExprSet &refs); +template <typename iterator> +void clearRefsIn(iterator begin, iterator end, ExprSet &refs) { + for (; begin != end; ++begin) + clearRefsIn(*begin, refs); +} + +void collectRefs(ValueDecl *D, Stmt *S, ExprSet &refs); + +void collectRemovables(Stmt *S, ExprSet &exprs); + +} // end namespace trans + +} // end namespace arcmt + +} // end namespace clang + +#endif diff --git a/lib/AST/ASTContext.cpp b/lib/AST/ASTContext.cpp index 9094abad2ef1..6eada6e22f4e 100644 --- a/lib/AST/ASTContext.cpp +++ b/lib/AST/ASTContext.cpp @@ -219,11 +219,12 @@ ASTContext::ASTContext(const LangOptions& LOpts, SourceManager &SM, FunctionProtoTypes(this_()), TemplateSpecializationTypes(this_()), DependentTemplateSpecializationTypes(this_()), + SubstTemplateTemplateParmPacks(this_()), GlobalNestedNameSpecifier(0), IsInt128Installed(false), CFConstantStringTypeDecl(0), NSConstantStringTypeDecl(0), - ObjCFastEnumerationStateTypeDecl(0), FILEDecl(0), jmp_bufDecl(0), - sigjmp_bufDecl(0), BlockDescriptorType(0), BlockDescriptorExtendedType(0), - cudaConfigureCallDecl(0), + ObjCFastEnumerationStateTypeDecl(0), FILEDecl(0), + jmp_bufDecl(0), sigjmp_bufDecl(0), BlockDescriptorType(0), + BlockDescriptorExtendedType(0), cudaConfigureCallDecl(0), NullTypeSourceInfo(QualType()), SourceMgr(SM), LangOpts(LOpts), ABI(createCXXABI(t)), AddrSpaceMap(getAddressSpaceMap(t, LOpts)), Target(t), @@ -288,8 +289,8 @@ ASTContext::setExternalSource(llvm::OwningPtr<ExternalASTSource> &Source) { } void ASTContext::PrintStats() const { - fprintf(stderr, "*** AST Context Stats:\n"); - fprintf(stderr, " %d types total.\n", (int)Types.size()); + llvm::errs() << "\n*** AST Context Stats:\n"; + llvm::errs() << " " << Types.size() << " types total.\n"; unsigned counts[] = { #define TYPE(Name, Parent) 0, @@ -307,40 +308,42 @@ void ASTContext::PrintStats() const { unsigned TotalBytes = 0; #define TYPE(Name, Parent) \ if (counts[Idx]) \ - fprintf(stderr, " %d %s types\n", (int)counts[Idx], #Name); \ + llvm::errs() << " " << counts[Idx] << " " << #Name \ + << " types\n"; \ TotalBytes += counts[Idx] * sizeof(Name##Type); \ ++Idx; #define ABSTRACT_TYPE(Name, Parent) #include "clang/AST/TypeNodes.def" - fprintf(stderr, "Total bytes = %d\n", int(TotalBytes)); - + llvm::errs() << "Total bytes = " << TotalBytes << "\n"; + // Implicit special member functions. - fprintf(stderr, " %u/%u implicit default constructors created\n", - NumImplicitDefaultConstructorsDeclared, - NumImplicitDefaultConstructors); - fprintf(stderr, " %u/%u implicit copy constructors created\n", - NumImplicitCopyConstructorsDeclared, - NumImplicitCopyConstructors); + llvm::errs() << NumImplicitDefaultConstructorsDeclared << "/" + << NumImplicitDefaultConstructors + << " implicit default constructors created\n"; + llvm::errs() << NumImplicitCopyConstructorsDeclared << "/" + << NumImplicitCopyConstructors + << " implicit copy constructors created\n"; if (getLangOptions().CPlusPlus) - fprintf(stderr, " %u/%u implicit move constructors created\n", - NumImplicitMoveConstructorsDeclared, - NumImplicitMoveConstructors); - fprintf(stderr, " %u/%u implicit copy assignment operators created\n", - NumImplicitCopyAssignmentOperatorsDeclared, - NumImplicitCopyAssignmentOperators); + llvm::errs() << NumImplicitMoveConstructorsDeclared << "/" + << NumImplicitMoveConstructors + << " implicit move constructors created\n"; + llvm::errs() << NumImplicitCopyAssignmentOperatorsDeclared << "/" + << NumImplicitCopyAssignmentOperators + << " implicit copy assignment operators created\n"; if (getLangOptions().CPlusPlus) - fprintf(stderr, " %u/%u implicit move assignment operators created\n", - NumImplicitMoveAssignmentOperatorsDeclared, - NumImplicitMoveAssignmentOperators); - fprintf(stderr, " %u/%u implicit destructors created\n", - NumImplicitDestructorsDeclared, NumImplicitDestructors); - + llvm::errs() << NumImplicitMoveAssignmentOperatorsDeclared << "/" + << NumImplicitMoveAssignmentOperators + << " implicit move assignment operators created\n"; + llvm::errs() << NumImplicitDestructorsDeclared << "/" + << NumImplicitDestructors + << " implicit destructors created\n"; + if (ExternalSource.get()) { - fprintf(stderr, "\n"); + llvm::errs() << "\n"; ExternalSource->PrintStats(); } - + BumpAlloc.PrintStats(); } @@ -753,7 +756,7 @@ ASTContext::getTypeInfo(const Type *T) const { #define NON_CANONICAL_TYPE(Class, Base) #define DEPENDENT_TYPE(Class, Base) case Type::Class: #include "clang/AST/TypeNodes.def" - assert(false && "Should not see dependent types"); + llvm_unreachable("Should not see dependent types"); break; case Type::FunctionNoProto: @@ -1089,8 +1092,12 @@ void ASTContext::DeepCollectObjCIvars(const ObjCInterfaceDecl *OI, E = OI->ivar_end(); I != E; ++I) Ivars.push_back(*I); } - else - ShallowCollectObjCIvars(OI, Ivars); + else { + ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(OI); + for (ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; + Iv= Iv->getNextIvar()) + Ivars.push_back(Iv); + } } /// CollectInheritedProtocols - Collect all protocols in current class and @@ -1881,7 +1888,7 @@ QualType ASTContext::getVectorType(QualType vecType, unsigned NumElts, /// the specified element type and size. VectorType must be a built-in type. QualType ASTContext::getExtVectorType(QualType vecType, unsigned NumElts) const { - assert(vecType->isBuiltinType()); + assert(vecType->isBuiltinType() || vecType->isDependentType()); // Check if we've already instantiated a vector of this type. llvm::FoldingSetNodeID ID; @@ -2040,10 +2047,13 @@ ASTContext::getFunctionType(QualType ResultTy, assert(NewIP == 0 && "Shouldn't be in the map!"); (void)NewIP; } - // FunctionProtoType objects are allocated with extra bytes after them - // for two variable size arrays (for parameter and exception types) at the - // end of them. Instead of the exception types, there could be a noexcept - // expression and a context pointer. + // FunctionProtoType objects are allocated with extra bytes after + // them for three variable size arrays at the end: + // - parameter types + // - exception types + // - consumed-arguments flags + // Instead of the exception types, there could be a noexcept + // expression. size_t Size = sizeof(FunctionProtoType) + NumArgs * sizeof(QualType); if (EPI.ExceptionSpecType == EST_Dynamic) @@ -2051,6 +2061,9 @@ ASTContext::getFunctionType(QualType ResultTy, else if (EPI.ExceptionSpecType == EST_ComputedNoexcept) { Size += sizeof(Expr*); } + if (EPI.ConsumedArguments) + Size += NumArgs * sizeof(bool); + FunctionProtoType *FTP = (FunctionProtoType*) Allocate(Size, TypeAlignment); FunctionProtoType::ExtProtoInfo newEPI = EPI; newEPI.ExtInfo = EPI.ExtInfo.withCallingConv(CallConv); @@ -2791,7 +2804,12 @@ static QualType getDecltypeForExpr(const Expr *e, const ASTContext &Context) { /// on canonical type's (which are always unique). QualType ASTContext::getDecltypeType(Expr *e) const { DecltypeType *dt; - if (e->isTypeDependent()) { + + // C++0x [temp.type]p2: + // If an expression e involves a template parameter, decltype(e) denotes a + // unique dependent type. Two such decltype-specifiers refer to the same + // type only if their expressions are equivalent (14.5.6.1). + if (e->isInstantiationDependent()) { llvm::FoldingSetNodeID ID; DependentDecltypeType::Profile(ID, *this, e); @@ -2925,7 +2943,6 @@ CanQualType ASTContext::getCanonicalParamType(QualType T) const { return CanQualType::CreateUnsafe(Result); } - QualType ASTContext::getUnqualifiedArrayType(QualType type, Qualifiers &quals) { SplitQualType splitType = type.getSplitUnqualifiedType(); @@ -3027,11 +3044,21 @@ bool ASTContext::UnwrapSimilarPointerTypes(QualType &T1, QualType &T2) { DeclarationNameInfo ASTContext::getNameForTemplate(TemplateName Name, SourceLocation NameLoc) const { - if (TemplateDecl *TD = Name.getAsTemplateDecl()) + switch (Name.getKind()) { + case TemplateName::QualifiedTemplate: + case TemplateName::Template: // DNInfo work in progress: CHECKME: what about DNLoc? - return DeclarationNameInfo(TD->getDeclName(), NameLoc); + return DeclarationNameInfo(Name.getAsTemplateDecl()->getDeclName(), + NameLoc); - if (DependentTemplateName *DTN = Name.getAsDependentTemplateName()) { + case TemplateName::OverloadedTemplate: { + OverloadedTemplateStorage *Storage = Name.getAsOverloadedTemplate(); + // DNInfo work in progress: CHECKME: what about DNLoc? + return DeclarationNameInfo((*Storage->begin())->getDeclName(), NameLoc); + } + + case TemplateName::DependentTemplate: { + DependentTemplateName *DTN = Name.getAsDependentTemplateName(); DeclarationName DName; if (DTN->isIdentifier()) { DName = DeclarationNames.getIdentifier(DTN->getIdentifier()); @@ -3046,36 +3073,64 @@ ASTContext::getNameForTemplate(TemplateName Name, } } - OverloadedTemplateStorage *Storage = Name.getAsOverloadedTemplate(); - assert(Storage); - // DNInfo work in progress: CHECKME: what about DNLoc? - return DeclarationNameInfo((*Storage->begin())->getDeclName(), NameLoc); + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *subst + = Name.getAsSubstTemplateTemplateParm(); + return DeclarationNameInfo(subst->getParameter()->getDeclName(), + NameLoc); + } + + case TemplateName::SubstTemplateTemplateParmPack: { + SubstTemplateTemplateParmPackStorage *subst + = Name.getAsSubstTemplateTemplateParmPack(); + return DeclarationNameInfo(subst->getParameterPack()->getDeclName(), + NameLoc); + } + } + + llvm_unreachable("bad template name kind!"); } TemplateName ASTContext::getCanonicalTemplateName(TemplateName Name) const { - if (TemplateDecl *Template = Name.getAsTemplateDecl()) { + switch (Name.getKind()) { + case TemplateName::QualifiedTemplate: + case TemplateName::Template: { + TemplateDecl *Template = Name.getAsTemplateDecl(); if (TemplateTemplateParmDecl *TTP - = dyn_cast<TemplateTemplateParmDecl>(Template)) + = dyn_cast<TemplateTemplateParmDecl>(Template)) Template = getCanonicalTemplateTemplateParmDecl(TTP); // The canonical template name is the canonical template declaration. return TemplateName(cast<TemplateDecl>(Template->getCanonicalDecl())); } - if (SubstTemplateTemplateParmPackStorage *SubstPack - = Name.getAsSubstTemplateTemplateParmPack()) { - TemplateTemplateParmDecl *CanonParam - = getCanonicalTemplateTemplateParmDecl(SubstPack->getParameterPack()); - TemplateArgument CanonArgPack - = getCanonicalTemplateArgument(SubstPack->getArgumentPack()); - return getSubstTemplateTemplateParmPack(CanonParam, CanonArgPack); + case TemplateName::OverloadedTemplate: + llvm_unreachable("cannot canonicalize overloaded template"); + + case TemplateName::DependentTemplate: { + DependentTemplateName *DTN = Name.getAsDependentTemplateName(); + assert(DTN && "Non-dependent template names must refer to template decls."); + return DTN->CanonicalTemplateName; + } + + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *subst + = Name.getAsSubstTemplateTemplateParm(); + return getCanonicalTemplateName(subst->getReplacement()); } - - assert(!Name.getAsOverloadedTemplate()); - DependentTemplateName *DTN = Name.getAsDependentTemplateName(); - assert(DTN && "Non-dependent template names must refer to template decls."); - return DTN->CanonicalTemplateName; + case TemplateName::SubstTemplateTemplateParmPack: { + SubstTemplateTemplateParmPackStorage *subst + = Name.getAsSubstTemplateTemplateParmPack(); + TemplateTemplateParmDecl *canonParameter + = getCanonicalTemplateTemplateParmDecl(subst->getParameterPack()); + TemplateArgument canonArgPack + = getCanonicalTemplateArgument(subst->getArgumentPack()); + return getSubstTemplateTemplateParmPack(canonParameter, canonArgPack); + } + } + + llvm_unreachable("bad template name!"); } bool ASTContext::hasSameTemplateName(TemplateName X, TemplateName Y) { @@ -3260,6 +3315,31 @@ const ArrayType *ASTContext::getAsArrayType(QualType T) const { VAT->getBracketsRange())); } +QualType ASTContext::getAdjustedParameterType(QualType T) { + // C99 6.7.5.3p7: + // A declaration of a parameter as "array of type" shall be + // adjusted to "qualified pointer to type", where the type + // qualifiers (if any) are those specified within the [ and ] of + // the array type derivation. + if (T->isArrayType()) + return getArrayDecayedType(T); + + // C99 6.7.5.3p8: + // A declaration of a parameter as "function returning type" + // shall be adjusted to "pointer to function returning type", as + // in 6.3.2.1. + if (T->isFunctionType()) + return getPointerType(T); + + return T; +} + +QualType ASTContext::getSignatureParameterType(QualType T) { + T = getVariableArrayDecayedType(T); + T = getAdjustedParameterType(T); + return T.getUnqualifiedType(); +} + /// getArrayDecayedType - Return the properly qualified result of decaying the /// specified array type to a pointer. This operation is non-trivial when /// handling typedefs etc. The canonical type of "T" must be an array type, @@ -3459,6 +3539,25 @@ QualType ASTContext::getPromotedIntegerType(QualType Promotable) const { return (PromotableSize != IntSize) ? IntTy : UnsignedIntTy; } +/// \brief Recurses in pointer/array types until it finds an objc retainable +/// type and returns its ownership. +Qualifiers::ObjCLifetime ASTContext::getInnerObjCOwnership(QualType T) const { + while (!T.isNull()) { + if (T.getObjCLifetime() != Qualifiers::OCL_None) + return T.getObjCLifetime(); + if (T->isArrayType()) + T = getBaseElementType(T); + else if (const PointerType *PT = T->getAs<PointerType>()) + T = PT->getPointeeType(); + else if (const ReferenceType *RT = T->getAs<ReferenceType>()) + T = RT->getPointeeType(); + else + break; + } + + return Qualifiers::OCL_None; +} + /// getIntegerTypeOrder - Returns the highest ranked integer type: /// C99 6.3.1.8p1. If LHS > RHS, return 1. If LHS == RHS, return 0. If /// LHS < RHS, return -1. @@ -3725,11 +3824,7 @@ void ASTContext::setBlockDescriptorExtendedType(QualType T) { } bool ASTContext::BlockRequiresCopying(QualType Ty) const { - if (Ty->isBlockPointerType()) - return true; - if (isObjCNSObjectType(Ty)) - return true; - if (Ty->isObjCObjectPointerType()) + if (Ty->isObjCRetainableType()) return true; if (getLangOptions().CPlusPlus) { if (const RecordType *RT = Ty->getAs<RecordType>()) { @@ -4180,17 +4275,7 @@ static void EncodeBitField(const ASTContext *Ctx, std::string& S, if (!Ctx->getLangOptions().NeXTRuntime) { const RecordDecl *RD = FD->getParent(); const ASTRecordLayout &RL = Ctx->getASTRecordLayout(RD); - // FIXME: This same linear search is also used in ExprConstant - it might - // be better if the FieldDecl stored its offset. We'd be increasing the - // size of the object slightly, but saving some time every time it is used. - unsigned i = 0; - for (RecordDecl::field_iterator Field = RD->field_begin(), - FieldEnd = RD->field_end(); - Field != FieldEnd; (void)++Field, ++i) { - if (*Field == FD) - break; - } - S += llvm::utostr(RL.getFieldOffset(i)); + S += llvm::utostr(RL.getFieldOffset(FD->getFieldIndex())); if (T->isEnumeralType()) S += 'i'; else @@ -4510,6 +4595,8 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, BE = CXXRec->bases_end(); BI != BE; ++BI) { if (!BI->isVirtual()) { CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl(); + if (base->isEmpty()) + continue; uint64_t offs = layout.getBaseClassOffsetInBits(base); FieldOrBaseOffsets.insert(FieldOrBaseOffsets.upper_bound(offs), std::make_pair(offs, base)); @@ -4531,6 +4618,8 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, BI = CXXRec->vbases_begin(), BE = CXXRec->vbases_end(); BI != BE; ++BI) { CXXRecordDecl *base = BI->getType()->getAsCXXRecordDecl(); + if (base->isEmpty()) + continue; uint64_t offs = layout.getVBaseClassOffsetInBits(base); FieldOrBaseOffsets.insert(FieldOrBaseOffsets.upper_bound(offs), std::make_pair(offs, base)); @@ -4594,8 +4683,8 @@ void ASTContext::getObjCEncodingForStructureImpl(RecordDecl *RDecl, // expands virtual bases each time one is encountered in the hierarchy, // making the encoding type bigger than it really is. getObjCEncodingForStructureImpl(base, S, FD, /*includeVBases*/false); - if (!base->isEmpty()) - CurOffs += toBits(getASTRecordLayout(base).getNonVirtualSize()); + assert(!base->isEmpty()); + CurOffs += toBits(getASTRecordLayout(base).getNonVirtualSize()); } else { FieldDecl *field = cast<FieldDecl>(dcl); if (FD) { @@ -4782,6 +4871,24 @@ ASTContext::getDependentTemplateName(NestedNameSpecifier *NNS, } TemplateName +ASTContext::getSubstTemplateTemplateParm(TemplateTemplateParmDecl *param, + TemplateName replacement) const { + llvm::FoldingSetNodeID ID; + SubstTemplateTemplateParmStorage::Profile(ID, param, replacement); + + void *insertPos = 0; + SubstTemplateTemplateParmStorage *subst + = SubstTemplateTemplateParms.FindNodeOrInsertPos(ID, insertPos); + + if (!subst) { + subst = new (*this) SubstTemplateTemplateParmStorage(param, replacement); + SubstTemplateTemplateParms.InsertNode(subst, insertPos); + } + + return TemplateName(subst); +} + +TemplateName ASTContext::getSubstTemplateTemplateParmPack(TemplateTemplateParmDecl *Param, const TemplateArgument &ArgPack) const { ASTContext &Self = const_cast<ASTContext &>(*this); @@ -4793,7 +4900,7 @@ ASTContext::getSubstTemplateTemplateParmPack(TemplateTemplateParmDecl *Param, = SubstTemplateTemplateParmPacks.FindNodeOrInsertPos(ID, InsertPos); if (!Subst) { - Subst = new (*this) SubstTemplateTemplateParmPackStorage(Self, Param, + Subst = new (*this) SubstTemplateTemplateParmPackStorage(Param, ArgPack.pack_size(), ArgPack.pack_begin()); SubstTemplateTemplateParmPacks.InsertNode(Subst, InsertPos); @@ -4826,20 +4933,6 @@ CanQualType ASTContext::getFromTargetType(unsigned Type) const { // Type Predicates. //===----------------------------------------------------------------------===// -/// isObjCNSObjectType - Return true if this is an NSObject object using -/// NSObject attribute on a c-style pointer type. -/// FIXME - Make it work directly on types. -/// FIXME: Move to Type. -/// -bool ASTContext::isObjCNSObjectType(QualType Ty) const { - if (const TypedefType *TDT = dyn_cast<TypedefType>(Ty)) { - if (TypedefNameDecl *TD = TDT->getDecl()) - if (TD->getAttr<ObjCNSObjectAttr>()) - return true; - } - return false; -} - /// getObjCGCAttr - Returns one of GCNone, Weak or Strong objc's /// garbage collection attribute. /// @@ -5348,6 +5441,10 @@ bool ASTContext::typesAreCompatible(QualType LHS, QualType RHS, return !mergeTypes(LHS, RHS, false, CompareUnqualified).isNull(); } +bool ASTContext::propertyTypesAreCompatible(QualType LHS, QualType RHS) { + return typesAreCompatible(LHS, RHS); +} + bool ASTContext::typesAreBlockPointerCompatible(QualType LHS, QualType RHS) { return !mergeTypes(LHS, RHS, true).isNull(); } @@ -5452,6 +5549,9 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (lbaseInfo.getRegParm() != rbaseInfo.getRegParm()) return QualType(); + if (lbaseInfo.getProducesResult() != rbaseInfo.getProducesResult()) + return QualType(); + // It's noreturn if either type is. // FIXME: some uses, e.g. conditional exprs, really want this to be 'both'. bool NoReturn = lbaseInfo.getNoReturn() || rbaseInfo.getNoReturn(); @@ -5460,10 +5560,7 @@ QualType ASTContext::mergeFunctionTypes(QualType lhs, QualType rhs, if (NoReturn != rbaseInfo.getNoReturn()) allRTypes = false; - FunctionType::ExtInfo einfo(NoReturn, - lbaseInfo.getHasRegParm(), - lbaseInfo.getRegParm(), - lbaseInfo.getCC()); + FunctionType::ExtInfo einfo = lbaseInfo.withNoReturn(NoReturn); if (lproto && rproto) { // two C99 style function prototypes assert(!lproto->hasExceptionSpec() && !rproto->hasExceptionSpec() && @@ -5584,7 +5681,8 @@ QualType ASTContext::mergeTypes(QualType LHS, QualType RHS, // If any of these qualifiers are different, we have a type // mismatch. if (LQuals.getCVRQualifiers() != RQuals.getCVRQualifiers() || - LQuals.getAddressSpace() != RQuals.getAddressSpace()) + LQuals.getAddressSpace() != RQuals.getAddressSpace() || + LQuals.getObjCLifetime() != RQuals.getObjCLifetime()) return QualType(); // Exactly one GC qualifier difference is allowed: __strong is @@ -6401,4 +6499,3 @@ size_t ASTContext::getSideTableAllocatedMemory() const { bytes += InstantiatedFromUnnamedFieldDecl.getMemorySize(); return bytes; } - diff --git a/lib/AST/ASTDiagnostic.cpp b/lib/AST/ASTDiagnostic.cpp index 16d2f853606e..7c91b5cb7a00 100644 --- a/lib/AST/ASTDiagnostic.cpp +++ b/lib/AST/ASTDiagnostic.cpp @@ -129,7 +129,7 @@ break; \ /// \brief Convert the given type to a string suitable for printing as part of /// a diagnostic. /// -/// There are three main criteria when determining whether we should have an +/// There are four main criteria when determining whether we should have an /// a.k.a. clause when pretty-printing a type: /// /// 1) Some types provide very minimal sugar that doesn't impede the @@ -142,15 +142,44 @@ break; \ /// want to desugar these, even if we do produce an a.k.a. clause. /// 3) Some types may have already been desugared previously in this diagnostic. /// if this is the case, doing another "aka" would just be clutter. +/// 4) Two different types within the same diagnostic have the same output +/// string. In this case, force an a.k.a with the desugared type when +/// doing so will provide additional information. /// /// \param Context the context in which the type was allocated /// \param Ty the type to print +/// \param QualTypeVals pointer values to QualTypes which are used in the +/// diagnostic message static std::string ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, const Diagnostic::ArgumentValue *PrevArgs, - unsigned NumPrevArgs) { + unsigned NumPrevArgs, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) { // FIXME: Playing with std::string is really slow. + bool ForceAKA = false; + QualType CanTy = Ty.getCanonicalType(); std::string S = Ty.getAsString(Context.PrintingPolicy); + std::string CanS = CanTy.getAsString(Context.PrintingPolicy); + + for (llvm::SmallVectorImpl<intptr_t>::iterator I = QualTypeVals.begin(), + E = QualTypeVals.end(); I != E; ++I) { + QualType CompareTy = + QualType::getFromOpaquePtr(reinterpret_cast<void*>(*I)); + if (CompareTy == Ty) + continue; // Same types + QualType CompareCanTy = CompareTy.getCanonicalType(); + if (CompareCanTy == CanTy) + continue; // Same canonical types + std::string CompareS = CompareTy.getAsString(Context.PrintingPolicy); + if (CompareS != S) + continue; // Original strings are different + std::string CompareCanS = CompareCanTy.getAsString(Context.PrintingPolicy); + if (CompareCanS == CanS) + continue; // No new info from canonical type + + ForceAKA = true; + break; + } // Check to see if we already desugared this type in this // diagnostic. If so, don't do it again. @@ -172,11 +201,15 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, if (!Repeated) { bool ShouldAKA = false; QualType DesugaredTy = Desugar(Context, Ty, ShouldAKA); - if (ShouldAKA) { - S = "'" + S + "' (aka '"; - S += DesugaredTy.getAsString(Context.PrintingPolicy); - S += "')"; - return S; + if (ShouldAKA || ForceAKA) { + if (DesugaredTy == Ty) { + DesugaredTy = Ty.getCanonicalType(); + } + std::string akaStr = DesugaredTy.getAsString(Context.PrintingPolicy); + if (akaStr != S) { + S = "'" + S + "' (aka '" + akaStr + "')"; + return S; + } } } @@ -184,16 +217,18 @@ ConvertTypeToDiagnosticString(ASTContext &Context, QualType Ty, return S; } -void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, - intptr_t Val, - const char *Modifier, - unsigned ModLen, - const char *Argument, - unsigned ArgLen, - const Diagnostic::ArgumentValue *PrevArgs, - unsigned NumPrevArgs, - llvm::SmallVectorImpl<char> &Output, - void *Cookie) { +void clang::FormatASTNodeDiagnosticArgument( + Diagnostic::ArgumentKind Kind, + intptr_t Val, + const char *Modifier, + unsigned ModLen, + const char *Argument, + unsigned ArgLen, + const Diagnostic::ArgumentValue *PrevArgs, + unsigned NumPrevArgs, + llvm::SmallVectorImpl<char> &Output, + void *Cookie, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) { ASTContext &Context = *static_cast<ASTContext*>(Cookie); std::string S; @@ -206,7 +241,8 @@ void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, "Invalid modifier for QualType argument"); QualType Ty(QualType::getFromOpaquePtr(reinterpret_cast<void*>(Val))); - S = ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, NumPrevArgs); + S = ConvertTypeToDiagnosticString(Context, Ty, PrevArgs, NumPrevArgs, + QualTypeVals); NeedQuotes = false; break; } @@ -257,7 +293,7 @@ void clang::FormatASTNodeDiagnosticArgument(Diagnostic::ArgumentKind Kind, } else if (TypeDecl *Type = dyn_cast<TypeDecl>(DC)) { S = ConvertTypeToDiagnosticString(Context, Context.getTypeDeclType(Type), - PrevArgs, NumPrevArgs); + PrevArgs, NumPrevArgs, QualTypeVals); } else { // FIXME: Get these strings from some localized place NamedDecl *ND = cast<NamedDecl>(DC); diff --git a/lib/AST/ASTImporter.cpp b/lib/AST/ASTImporter.cpp index 100e604d1c46..f5e392f88d0b 100644 --- a/lib/AST/ASTImporter.cpp +++ b/lib/AST/ASTImporter.cpp @@ -4201,6 +4201,20 @@ TemplateName ASTImporter::Import(TemplateName From) { return ToContext.getDependentTemplateName(Qualifier, DTN->getOperator()); } + + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *subst + = From.getAsSubstTemplateTemplateParm(); + TemplateTemplateParmDecl *param + = cast_or_null<TemplateTemplateParmDecl>(Import(subst->getParameter())); + if (!param) + return TemplateName(); + + TemplateName replacement = Import(subst->getReplacement()); + if (replacement.isNull()) return TemplateName(); + + return ToContext.getSubstTemplateTemplateParm(param, replacement); + } case TemplateName::SubstTemplateTemplateParmPack: { SubstTemplateTemplateParmPackStorage *SubstPack @@ -4232,8 +4246,8 @@ SourceLocation ASTImporter::Import(SourceLocation FromLoc) { SourceManager &FromSM = FromContext.getSourceManager(); // For now, map everything down to its spelling location, so that we - // don't have to import macro instantiations. - // FIXME: Import macro instantiations! + // don't have to import macro expansions. + // FIXME: Import macro expansions! FromLoc = FromSM.getSpellingLoc(FromLoc); std::pair<FileID, unsigned> Decomposed = FromSM.getDecomposedLoc(FromLoc); SourceManager &ToSM = ToContext.getSourceManager(); @@ -4254,7 +4268,7 @@ FileID ASTImporter::Import(FileID FromID) { SourceManager &FromSM = FromContext.getSourceManager(); SourceManager &ToSM = ToContext.getSourceManager(); const SrcMgr::SLocEntry &FromSLoc = FromSM.getSLocEntry(FromID); - assert(FromSLoc.isFile() && "Cannot handle macro instantiations yet"); + assert(FromSLoc.isFile() && "Cannot handle macro expansions yet"); // Include location of this file. SourceLocation ToIncludeLoc = Import(FromSLoc.getFile().getIncludeLoc()); diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index 12357c07a794..4c323da7eee3 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -197,6 +197,16 @@ getLVForTemplateArgumentList(const TemplateArgumentList &TArgs, return getLVForTemplateArgumentList(TArgs.data(), TArgs.size(), F); } +static bool shouldConsiderTemplateLV(const FunctionDecl *fn, + const FunctionTemplateSpecializationInfo *spec) { + return !(spec->isExplicitSpecialization() && + fn->hasAttr<VisibilityAttr>()); +} + +static bool shouldConsiderTemplateLV(const ClassTemplateSpecializationDecl *d) { + return !(d->isExplicitSpecialization() && d->hasAttr<VisibilityAttr>()); +} + static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { assert(D->getDeclContext()->getRedeclContext()->isFileContext() && "Not a name having namespace scope"); @@ -231,6 +241,14 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { if (!FoundExtern) return LinkageInfo::internal(); } + if (Var->getStorageClass() == SC_None) { + const VarDecl *PrevVar = Var->getPreviousDeclaration(); + for (; PrevVar; PrevVar = PrevVar->getPreviousDeclaration()) + if (PrevVar->getStorageClass() == SC_PrivateExtern) + break; + if (PrevVar) + return PrevVar->getLinkageAndVisibility(); + } } else if (isa<FunctionDecl>(D) || isa<FunctionTemplateDecl>(D)) { // C++ [temp]p4: // A non-member function template can have internal linkage; any @@ -389,12 +407,16 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { Function->getType()->getLinkage() == UniqueExternalLinkage) return LinkageInfo::uniqueExternal(); - if (FunctionTemplateSpecializationInfo *SpecInfo + // Consider LV from the template and the template arguments unless + // this is an explicit specialization with a visibility attribute. + if (FunctionTemplateSpecializationInfo *specInfo = Function->getTemplateSpecializationInfo()) { - LV.merge(getLVForDecl(SpecInfo->getTemplate(), - F.onlyTemplateVisibility())); - const TemplateArgumentList &TemplateArgs = *SpecInfo->TemplateArguments; - LV.merge(getLVForTemplateArgumentList(TemplateArgs, F)); + if (shouldConsiderTemplateLV(Function, specInfo)) { + LV.merge(getLVForDecl(specInfo->getTemplate(), + F.onlyTemplateVisibility())); + const TemplateArgumentList &templateArgs = *specInfo->TemplateArguments; + LV.merge(getLVForTemplateArgumentList(templateArgs, F)); + } } // - a named class (Clause 9), or an unnamed class defined in a @@ -410,15 +432,17 @@ static LinkageInfo getLVForNamespaceScopeDecl(const NamedDecl *D, LVFlags F) { // If this is a class template specialization, consider the // linkage of the template and template arguments. - if (const ClassTemplateSpecializationDecl *Spec + if (const ClassTemplateSpecializationDecl *spec = dyn_cast<ClassTemplateSpecializationDecl>(Tag)) { - // From the template. - LV.merge(getLVForDecl(Spec->getSpecializedTemplate(), - F.onlyTemplateVisibility())); - - // The arguments at which the template was instantiated. - const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); - LV.merge(getLVForTemplateArgumentList(TemplateArgs, F)); + if (shouldConsiderTemplateLV(spec)) { + // From the template. + LV.merge(getLVForDecl(spec->getSpecializedTemplate(), + F.onlyTemplateVisibility())); + + // The arguments at which the template was instantiated. + const TemplateArgumentList &TemplateArgs = spec->getTemplateArgs(); + LV.merge(getLVForTemplateArgumentList(TemplateArgs, F)); + } } // Consider -fvisibility unless the type has C linkage. @@ -519,14 +543,16 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { // If this is a method template specialization, use the linkage for // the template parameters and arguments. - if (FunctionTemplateSpecializationInfo *Spec + if (FunctionTemplateSpecializationInfo *spec = MD->getTemplateSpecializationInfo()) { - LV.merge(getLVForTemplateArgumentList(*Spec->TemplateArguments, F)); - if (F.ConsiderTemplateParameterTypes) - LV.merge(getLVForTemplateParameterList( - Spec->getTemplate()->getTemplateParameters())); + if (shouldConsiderTemplateLV(MD, spec)) { + LV.merge(getLVForTemplateArgumentList(*spec->TemplateArguments, F)); + if (F.ConsiderTemplateParameterTypes) + LV.merge(getLVForTemplateParameterList( + spec->getTemplate()->getTemplateParameters())); + } - TSK = Spec->getTemplateSpecializationKind(); + TSK = spec->getTemplateSpecializationKind(); } else if (MemberSpecializationInfo *MSI = MD->getMemberSpecializationInfo()) { TSK = MSI->getTemplateSpecializationKind(); @@ -553,14 +579,16 @@ static LinkageInfo getLVForClassMember(const NamedDecl *D, LVFlags F) { // *do* apply -fvisibility to method declarations. } else if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(D)) { - if (const ClassTemplateSpecializationDecl *Spec + if (const ClassTemplateSpecializationDecl *spec = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { - // Merge template argument/parameter information for member - // class template specializations. - LV.merge(getLVForTemplateArgumentList(Spec->getTemplateArgs(), F)); + if (shouldConsiderTemplateLV(spec)) { + // Merge template argument/parameter information for member + // class template specializations. + LV.merge(getLVForTemplateArgumentList(spec->getTemplateArgs(), F)); if (F.ConsiderTemplateParameterTypes) LV.merge(getLVForTemplateParameterList( - Spec->getSpecializedTemplate()->getTemplateParameters())); + spec->getSpecializedTemplate()->getTemplateParameters())); + } } // Static data members. @@ -1304,6 +1332,19 @@ void VarDecl::setInit(Expr *I) { Init = I; } +bool VarDecl::extendsLifetimeOfTemporary() const { + assert(getType()->isReferenceType() &&"Non-references never extend lifetime"); + + const Expr *E = getInit(); + if (!E) + return false; + + if (const ExprWithCleanups *Cleanups = dyn_cast<ExprWithCleanups>(E)) + E = Cleanups->getSubExpr(); + + return isa<MaterializeTemporaryExpr>(E); +} + VarDecl *VarDecl::getInstantiatedFromStaticDataMember() const { if (MemberSpecializationInfo *MSI = getMemberSpecializationInfo()) return cast<VarDecl>(MSI->getInstantiatedFrom()); @@ -2320,8 +2361,15 @@ void RecordDecl::LoadFieldsFromExternalStorage() const { ExternalASTSource::Deserializing TheFields(Source); llvm::SmallVector<Decl*, 64> Decls; - if (Source->FindExternalLexicalDeclsBy<FieldDecl>(this, Decls)) + LoadedFieldsFromExternalStorage = true; + switch (Source->FindExternalLexicalDeclsBy<FieldDecl>(this, Decls)) { + case ELR_Success: + break; + + case ELR_AlreadyLoaded: + case ELR_Failure: return; + } #ifndef NDEBUG // Check that all decls we got were FieldDecls. @@ -2329,8 +2377,6 @@ void RecordDecl::LoadFieldsFromExternalStorage() const { assert(isa<FieldDecl>(Decls[i])); #endif - LoadedFieldsFromExternalStorage = true; - if (Decls.empty()) return; @@ -2376,6 +2422,16 @@ void BlockDecl::setCaptures(ASTContext &Context, Captures = static_cast<Capture*>(buffer); } +bool BlockDecl::capturesVariable(const VarDecl *variable) const { + for (capture_const_iterator + i = capture_begin(), e = capture_end(); i != e; ++i) + // Only auto vars can be captured, so no redeclaration worries. + if (i->getVariable() == variable) + return true; + + return false; +} + SourceRange BlockDecl::getSourceRange() const { return SourceRange(getLocation(), Body? Body->getLocEnd() : getLocation()); } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 1766d39c1405..b2806f092cbd 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -29,7 +29,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> -#include <cstdio> using namespace clang; //===----------------------------------------------------------------------===// @@ -76,26 +75,27 @@ bool Decl::CollectingStats(bool Enable) { } void Decl::PrintStats() { - fprintf(stderr, "*** Decl Stats:\n"); + llvm::errs() << "\n*** Decl Stats:\n"; int totalDecls = 0; #define DECL(DERIVED, BASE) totalDecls += n##DERIVED##s; #define ABSTRACT_DECL(DECL) #include "clang/AST/DeclNodes.inc" - fprintf(stderr, " %d decls total.\n", totalDecls); + llvm::errs() << " " << totalDecls << " decls total.\n"; int totalBytes = 0; #define DECL(DERIVED, BASE) \ if (n##DERIVED##s > 0) { \ totalBytes += (int)(n##DERIVED##s * sizeof(DERIVED##Decl)); \ - fprintf(stderr, " %d " #DERIVED " decls, %d each (%d bytes)\n", \ - n##DERIVED##s, (int)sizeof(DERIVED##Decl), \ - (int)(n##DERIVED##s * sizeof(DERIVED##Decl))); \ + llvm::errs() << " " << n##DERIVED##s << " " #DERIVED " decls, " \ + << sizeof(DERIVED##Decl) << " each (" \ + << n##DERIVED##s * sizeof(DERIVED##Decl) \ + << " bytes)\n"; \ } #define ABSTRACT_DECL(DECL) #include "clang/AST/DeclNodes.inc" - fprintf(stderr, "Total bytes = %d\n", totalBytes); + llvm::errs() << "Total bytes = " << totalBytes << "\n"; } void Decl::add(Kind k) { @@ -641,12 +641,8 @@ DeclContext *Decl::getNonClosureContext() { // This is basically "while (DC->isClosure()) DC = DC->getParent();" // except that it's significantly more efficient to cast to a known // decl type and call getDeclContext() than to call getParent(). - do { - if (isa<BlockDecl>(DC)) { - DC = cast<BlockDecl>(DC)->getDeclContext(); - continue; - } - } while (false); + while (isa<BlockDecl>(DC)) + DC = cast<BlockDecl>(DC)->getDeclContext(); assert(!DC->isClosure()); return DC; @@ -843,12 +839,17 @@ DeclContext::LoadLexicalDeclsFromExternalStorage() const { // Notify that we have a DeclContext that is initializing. ExternalASTSource::Deserializing ADeclContext(Source); + // Load the external declarations, if any. llvm::SmallVector<Decl*, 64> Decls; - if (Source->FindExternalLexicalDecls(this, Decls)) - return; - - // There is no longer any lexical storage in this context ExternalLexicalStorage = false; + switch (Source->FindExternalLexicalDecls(this, Decls)) { + case ELR_Success: + break; + + case ELR_Failure: + case ELR_AlreadyLoaded: + return; + } if (Decls.empty()) return; diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 08ac2a5be4d9..4b59bf37d74b 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -228,6 +228,11 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, if (!BaseClassDecl->hasTrivialDestructor()) data().HasTrivialDestructor = false; + // A class has an Objective-C object member if... or any of its bases + // has an Objective-C object member. + if (BaseClassDecl->hasObjectMember()) + setHasObjectMember(true); + // Keep track of the presence of mutable fields. if (BaseClassDecl->hasMutableFields()) data().HasMutableFields = true; @@ -239,22 +244,8 @@ CXXRecordDecl::setBases(CXXBaseSpecifier const * const *Bases, // Create base specifier for any direct or indirect virtual bases. data().VBases = new (C) CXXBaseSpecifier[VBases.size()]; data().NumVBases = VBases.size(); - for (int I = 0, E = VBases.size(); I != E; ++I) { - TypeSourceInfo *VBaseTypeInfo = VBases[I]->getTypeSourceInfo(); - - // Skip dependent types; we can't do any checking on them now. - if (VBaseTypeInfo->getType()->isDependentType()) - continue; - - CXXRecordDecl *VBaseClassDecl = cast<CXXRecordDecl>( - VBaseTypeInfo->getType()->getAs<RecordType>()->getDecl()); - - data().getVBases()[I] = - CXXBaseSpecifier(VBaseClassDecl->getSourceRange(), true, - VBaseClassDecl->getTagKind() == TTK_Class, - VBases[I]->getAccessSpecifier(), VBaseTypeInfo, - SourceLocation()); - } + for (int I = 0, E = VBases.size(); I != E; ++I) + data().getVBases()[I] = *VBases[I]; } /// Callback function for CXXRecordDecl::forallBases that acknowledges @@ -698,10 +689,23 @@ NotASpecialMember:; // A POD struct is a class that is both a trivial class and a // standard-layout class, and has no non-static data members of type // non-POD struct, non-POD union (or array of such types). + // + // Automatic Reference Counting: the presence of a member of Objective-C pointer type + // that does not explicitly have no lifetime makes the class a non-POD. + // However, we delay setting PlainOldData to false in this case so that + // Sema has a chance to diagnostic causes where the same class will be + // non-POD with Automatic Reference Counting but a POD without Instant Objects. + // In this case, the class will become a non-POD class when we complete + // the definition. ASTContext &Context = getASTContext(); QualType T = Context.getBaseElementType(Field->getType()); - if (!T->isPODType()) + if (T->isObjCRetainableType() || T.isObjCGCStrong()) { + if (!Context.getLangOptions().ObjCAutoRefCount || + T.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) + setHasObjectMember(true); + } else if (!T.isPODType(Context)) data().PlainOldData = false; + if (T->isReferenceType()) { data().HasTrivialDefaultConstructor = false; @@ -768,6 +772,8 @@ NotASpecialMember:; if (!FieldRec->hasTrivialDestructor()) data().HasTrivialDestructor = false; + if (FieldRec->hasObjectMember()) + setHasObjectMember(true); // C++0x [class]p7: // A standard-layout class is a class that: @@ -1078,6 +1084,20 @@ void CXXRecordDecl::completeDefinition() { void CXXRecordDecl::completeDefinition(CXXFinalOverriderMap *FinalOverriders) { RecordDecl::completeDefinition(); + if (hasObjectMember() && getASTContext().getLangOptions().ObjCAutoRefCount) { + // Objective-C Automatic Reference Counting: + // If a class has a non-static data member of Objective-C pointer + // type (or array thereof), it is a non-POD type and its + // default constructor (if any), copy constructor, copy assignment + // operator, and destructor are non-trivial. + struct DefinitionData &Data = data(); + Data.PlainOldData = false; + Data.HasTrivialDefaultConstructor = false; + Data.HasTrivialCopyConstructor = false; + Data.HasTrivialCopyAssignment = false; + Data.HasTrivialDestructor = false; + } + // If the class may be abstract (but hasn't been marked as such), check for // any pure final overriders. if (mayBeAbstract()) { diff --git a/lib/AST/DeclObjC.cpp b/lib/AST/DeclObjC.cpp index e2c4f38ff9be..557b681d2fa6 100644 --- a/lib/AST/DeclObjC.cpp +++ b/lib/AST/DeclObjC.cpp @@ -452,6 +452,34 @@ ObjCMethodFamily ObjCMethodDecl::getMethodFamily() const { if (!isInstanceMethod()) family = OMF_None; break; + + case OMF_performSelector: + if (!isInstanceMethod() || + !getResultType()->isObjCIdType()) + family = OMF_None; + else { + unsigned noParams = param_size(); + if (noParams < 1 || noParams > 3) + family = OMF_None; + else { + ObjCMethodDecl::arg_type_iterator it = arg_type_begin(); + QualType ArgT = (*it); + if (!ArgT->isObjCSelType()) { + family = OMF_None; + break; + } + while (--noParams) { + it++; + ArgT = (*it); + if (!ArgT->isObjCIdType()) { + family = OMF_None; + break; + } + } + } + } + break; + } // Cache the result. @@ -474,8 +502,34 @@ void ObjCMethodDecl::createImplicitParams(ASTContext &Context, } else // we have a factory method. selfTy = Context.getObjCClassType(); - setSelfDecl(ImplicitParamDecl::Create(Context, this, SourceLocation(), - &Context.Idents.get("self"), selfTy)); + bool selfIsPseudoStrong = false; + bool selfIsConsumed = false; + if (isInstanceMethod() && Context.getLangOptions().ObjCAutoRefCount) { + selfIsConsumed = hasAttr<NSConsumesSelfAttr>(); + + // 'self' is always __strong. It's actually pseudo-strong except + // in init methods, though. + Qualifiers qs; + qs.setObjCLifetime(Qualifiers::OCL_Strong); + selfTy = Context.getQualifiedType(selfTy, qs); + + // In addition, 'self' is const unless this is an init method. + if (getMethodFamily() != OMF_init) { + selfTy = selfTy.withConst(); + selfIsPseudoStrong = true; + } + } + + ImplicitParamDecl *self + = ImplicitParamDecl::Create(Context, this, SourceLocation(), + &Context.Idents.get("self"), selfTy); + setSelfDecl(self); + + if (selfIsConsumed) + self->addAttr(new (Context) NSConsumedAttr(SourceLocation(), Context)); + + if (selfIsPseudoStrong) + self->setARCPseudoStrong(true); setCmdDecl(ImplicitParamDecl::Create(Context, this, SourceLocation(), &Context.Idents.get("_cmd"), diff --git a/lib/AST/DeclPrinter.cpp b/lib/AST/DeclPrinter.cpp index 421770ea70fb..19554a3baaea 100644 --- a/lib/AST/DeclPrinter.cpp +++ b/lib/AST/DeclPrinter.cpp @@ -933,6 +933,11 @@ void DeclPrinter::VisitObjCPropertyDecl(ObjCPropertyDecl *PDecl) { first = false; } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_strong) { + Out << (first ? ' ' : ',') << "strong"; + first = false; + } + if (PDecl->getPropertyAttributes() & ObjCPropertyDecl::OBJC_PR_copy) { Out << (first ? ' ' : ',') << "copy"; first = false; diff --git a/lib/AST/DeclarationName.cpp b/lib/AST/DeclarationName.cpp index cef54e97c93a..72c0e9da7f1d 100644 --- a/lib/AST/DeclarationName.cpp +++ b/lib/AST/DeclarationName.cpp @@ -533,6 +533,28 @@ bool DeclarationNameInfo::containsUnexpandedParameterPack() const { llvm_unreachable("All name kinds handled."); } +bool DeclarationNameInfo::isInstantiationDependent() const { + switch (Name.getNameKind()) { + case DeclarationName::Identifier: + case DeclarationName::ObjCZeroArgSelector: + case DeclarationName::ObjCOneArgSelector: + case DeclarationName::ObjCMultiArgSelector: + case DeclarationName::CXXOperatorName: + case DeclarationName::CXXLiteralOperatorName: + case DeclarationName::CXXUsingDirective: + return false; + + case DeclarationName::CXXConstructorName: + case DeclarationName::CXXDestructorName: + case DeclarationName::CXXConversionFunctionName: + if (TypeSourceInfo *TInfo = LocInfo.NamedType.TInfo) + return TInfo->getType()->isInstantiationDependentType(); + + return Name.getCXXNameType()->isInstantiationDependentType(); + } + llvm_unreachable("All name kinds handled."); +} + std::string DeclarationNameInfo::getAsString() const { std::string Result; llvm::raw_string_ostream OS(Result); diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp index 987213907e45..4611ae369969 100644 --- a/lib/AST/Expr.cpp +++ b/lib/AST/Expr.cpp @@ -142,9 +142,10 @@ void ExplicitTemplateArgumentList::initializeFrom( } void ExplicitTemplateArgumentList::initializeFrom( - const TemplateArgumentListInfo &Info, - bool &Dependent, - bool &ContainsUnexpandedParameterPack) { + const TemplateArgumentListInfo &Info, + bool &Dependent, + bool &InstantiationDependent, + bool &ContainsUnexpandedParameterPack) { LAngleLoc = Info.getLAngleLoc(); RAngleLoc = Info.getRAngleLoc(); NumTemplateArgs = Info.size(); @@ -152,6 +153,8 @@ void ExplicitTemplateArgumentList::initializeFrom( TemplateArgumentLoc *ArgBuffer = getTemplateArgs(); for (unsigned i = 0; i != NumTemplateArgs; ++i) { Dependent = Dependent || Info[i].getArgument().isDependent(); + InstantiationDependent = InstantiationDependent || + Info[i].getArgument().isInstantiationDependent(); ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack || Info[i].getArgument().containsUnexpandedParameterPack(); @@ -178,14 +181,16 @@ std::size_t ExplicitTemplateArgumentList::sizeFor( return sizeFor(Info.size()); } -/// \brief Compute the type- and value-dependence of a declaration reference +/// \brief Compute the type-, value-, and instantiation-dependence of a +/// declaration reference /// based on the declaration being referenced. static void computeDeclRefDependence(NamedDecl *D, QualType T, bool &TypeDependent, - bool &ValueDependent) { + bool &ValueDependent, + bool &InstantiationDependent) { TypeDependent = false; ValueDependent = false; - + InstantiationDependent = false; // (TD) C++ [temp.dep.expr]p3: // An id-expression is type-dependent if it contains: @@ -200,20 +205,31 @@ static void computeDeclRefDependence(NamedDecl *D, QualType T, if (T->isDependentType()) { TypeDependent = true; ValueDependent = true; + InstantiationDependent = true; return; + } else if (T->isInstantiationDependentType()) { + InstantiationDependent = true; } // (TD) - a conversion-function-id that specifies a dependent type if (D->getDeclName().getNameKind() - == DeclarationName::CXXConversionFunctionName && - D->getDeclName().getCXXNameType()->isDependentType()) { - TypeDependent = true; - ValueDependent = true; - return; + == DeclarationName::CXXConversionFunctionName) { + QualType T = D->getDeclName().getCXXNameType(); + if (T->isDependentType()) { + TypeDependent = true; + ValueDependent = true; + InstantiationDependent = true; + return; + } + + if (T->isInstantiationDependentType()) + InstantiationDependent = true; } + // (VD) - the name of a non-type template parameter, if (isa<NonTypeTemplateParmDecl>(D)) { ValueDependent = true; + InstantiationDependent = true; return; } @@ -223,16 +239,20 @@ static void computeDeclRefDependence(NamedDecl *D, QualType T, if (Var->getType()->isIntegralOrEnumerationType() && Var->getType().getCVRQualifiers() == Qualifiers::Const) { if (const Expr *Init = Var->getAnyInitializer()) - if (Init->isValueDependent()) + if (Init->isValueDependent()) { ValueDependent = true; + InstantiationDependent = true; + } } // (VD) - FIXME: Missing from the standard: // - a member function or a static data member of the current // instantiation else if (Var->isStaticDataMember() && - Var->getDeclContext()->isDependentContext()) + Var->getDeclContext()->isDependentContext()) { ValueDependent = true; + InstantiationDependent = true; + } return; } @@ -242,6 +262,7 @@ static void computeDeclRefDependence(NamedDecl *D, QualType T, // instantiation if (isa<CXXMethodDecl>(D) && D->getDeclContext()->isDependentContext()) { ValueDependent = true; + InstantiationDependent = true; return; } } @@ -249,7 +270,9 @@ static void computeDeclRefDependence(NamedDecl *D, QualType T, void DeclRefExpr::computeDependence() { bool TypeDependent = false; bool ValueDependent = false; - computeDeclRefDependence(getDecl(), getType(), TypeDependent, ValueDependent); + bool InstantiationDependent = false; + computeDeclRefDependence(getDecl(), getType(), TypeDependent, ValueDependent, + InstantiationDependent); // (TD) C++ [temp.dep.expr]p3: // An id-expression is type-dependent if it contains: @@ -262,13 +285,16 @@ void DeclRefExpr::computeDependence() { hasExplicitTemplateArgs() && TemplateSpecializationType::anyDependentTemplateArguments( getTemplateArgs(), - getNumTemplateArgs())) { + getNumTemplateArgs(), + InstantiationDependent)) { TypeDependent = true; ValueDependent = true; + InstantiationDependent = true; } ExprBits.TypeDependent = TypeDependent; ExprBits.ValueDependent = ValueDependent; + ExprBits.InstantiationDependent = InstantiationDependent; // Is the declaration a parameter pack? if (getDecl()->isParameterPack()) @@ -280,7 +306,7 @@ DeclRefExpr::DeclRefExpr(NestedNameSpecifierLoc QualifierLoc, NamedDecl *FoundD, const TemplateArgumentListInfo *TemplateArgs, QualType T, ExprValueKind VK) - : Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false), + : Expr(DeclRefExprClass, T, VK, OK_Ordinary, false, false, false, false), D(D), Loc(NameInfo.getLoc()), DNLoc(NameInfo.getInfo()) { DeclRefExprBits.HasQualifier = QualifierLoc ? 1 : 0; if (QualifierLoc) @@ -289,9 +315,17 @@ DeclRefExpr::DeclRefExpr(NestedNameSpecifierLoc QualifierLoc, if (FoundD) getInternalFoundDecl() = FoundD; DeclRefExprBits.HasExplicitTemplateArgs = TemplateArgs ? 1 : 0; - if (TemplateArgs) - getExplicitTemplateArgs().initializeFrom(*TemplateArgs); - + if (TemplateArgs) { + bool Dependent = false; + bool InstantiationDependent = false; + bool ContainsUnexpandedParameterPack = false; + getExplicitTemplateArgs().initializeFrom(*TemplateArgs, Dependent, + InstantiationDependent, + ContainsUnexpandedParameterPack); + if (InstantiationDependent) + setInstantiationDependent(true); + } + computeDependence(); } @@ -498,8 +532,8 @@ double FloatingLiteral::getValueAsApproximateDouble() const { return V.convertToDouble(); } -StringLiteral *StringLiteral::Create(ASTContext &C, const char *StrData, - unsigned ByteLength, bool Wide, +StringLiteral *StringLiteral::Create(ASTContext &C, llvm::StringRef Str, + bool Wide, bool Pascal, QualType Ty, const SourceLocation *Loc, unsigned NumStrs) { @@ -511,10 +545,10 @@ StringLiteral *StringLiteral::Create(ASTContext &C, const char *StrData, StringLiteral *SL = new (Mem) StringLiteral(Ty); // OPTIMIZE: could allocate this appended to the StringLiteral. - char *AStrData = new (C, 1) char[ByteLength]; - memcpy(AStrData, StrData, ByteLength); + char *AStrData = new (C, 1) char[Str.size()]; + memcpy(AStrData, Str.data(), Str.size()); SL->StrData = AStrData; - SL->ByteLength = ByteLength; + SL->ByteLength = Str.size(); SL->IsWide = Wide; SL->IsPascal = Pascal; SL->TokLocs[0] = Loc[0]; @@ -593,7 +627,7 @@ getLocationOfByte(unsigned ByteNo, const SourceManager &SM, // If the byte is in this token, return the location of the byte. if (ByteNo < TokNumBytes || - (ByteNo == TokNumBytes && TokNo == getNumConcatenated())) { + (ByteNo == TokNumBytes && TokNo == getNumConcatenated() - 1)) { unsigned Offset = SLP.getOffsetOfStringByte(TheTok, ByteNo); // Now that we know the offset of the token in the spelling, use the @@ -670,6 +704,7 @@ CallExpr::CallExpr(ASTContext& C, StmtClass SC, Expr *fn, unsigned NumPreArgs, : Expr(SC, t, VK, OK_Ordinary, fn->isTypeDependent(), fn->isValueDependent(), + fn->isInstantiationDependent(), fn->containsUnexpandedParameterPack()), NumArgs(numargs) { @@ -680,6 +715,8 @@ CallExpr::CallExpr(ASTContext& C, StmtClass SC, Expr *fn, unsigned NumPreArgs, ExprBits.TypeDependent = true; if (args[i]->isValueDependent()) ExprBits.ValueDependent = true; + if (args[i]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (args[i]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -695,6 +732,7 @@ CallExpr::CallExpr(ASTContext& C, Expr *fn, Expr **args, unsigned numargs, : Expr(CallExprClass, t, VK, OK_Ordinary, fn->isTypeDependent(), fn->isValueDependent(), + fn->isInstantiationDependent(), fn->containsUnexpandedParameterPack()), NumArgs(numargs) { @@ -705,6 +743,8 @@ CallExpr::CallExpr(ASTContext& C, Expr *fn, Expr **args, unsigned numargs, ExprBits.TypeDependent = true; if (args[i]->isValueDependent()) ExprBits.ValueDependent = true; + if (args[i]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (args[i]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -862,6 +902,7 @@ OffsetOfExpr::OffsetOfExpr(ASTContext &C, QualType type, : Expr(OffsetOfExprClass, type, VK_RValue, OK_Ordinary, /*TypeDependent=*/false, /*ValueDependent=*/tsi->getType()->isDependentType(), + tsi->getType()->isInstantiationDependentType(), tsi->getType()->containsUnexpandedParameterPack()), OperatorLoc(OperatorLoc), RParenLoc(RParenLoc), TSInfo(tsi), NumComps(numComps), NumExprs(numExprs) @@ -917,7 +958,12 @@ MemberExpr *MemberExpr::Create(ASTContext &C, Expr *base, bool isarrow, if (QualifierLoc && QualifierLoc.getNestedNameSpecifier()->isDependent()) { E->setValueDependent(true); E->setTypeDependent(true); - } + E->setInstantiationDependent(true); + } + else if (QualifierLoc && + QualifierLoc.getNestedNameSpecifier()->isInstantiationDependent()) + E->setInstantiationDependent(true); + E->HasQualifierOrFoundDecl = true; MemberNameQualifier *NQ = E->getMemberQualifier(); @@ -926,8 +972,15 @@ MemberExpr *MemberExpr::Create(ASTContext &C, Expr *base, bool isarrow, } if (targs) { + bool Dependent = false; + bool InstantiationDependent = false; + bool ContainsUnexpandedParameterPack = false; E->HasExplicitTemplateArgumentList = true; - E->getExplicitTemplateArgs().initializeFrom(*targs); + E->getExplicitTemplateArgs().initializeFrom(*targs, Dependent, + InstantiationDependent, + ContainsUnexpandedParameterPack); + if (InstantiationDependent) + E->setInstantiationDependent(true); } return E; @@ -1045,6 +1098,12 @@ const char *CastExpr::getCastKindName() const { return "IntegralComplexCast"; case CK_IntegralComplexToFloatingComplex: return "IntegralComplexToFloatingComplex"; + case CK_ObjCConsumeObject: + return "ObjCConsumeObject"; + case CK_ObjCProduceObject: + return "ObjCProduceObject"; + case CK_ObjCReclaimReturnedObject: + return "ObjCReclaimReturnedObject"; } llvm_unreachable("Unhandled cast kind!"); @@ -1056,7 +1115,12 @@ Expr *CastExpr::getSubExprAsWritten() { CastExpr *E = this; do { SubExpr = E->getSubExpr(); - + + // Skip through reference binding to temporary. + if (MaterializeTemporaryExpr *Materialize + = dyn_cast<MaterializeTemporaryExpr>(SubExpr)) + SubExpr = Materialize->GetTemporaryExpr(); + // Skip any temporary bindings; they're implicit. if (CXXBindTemporaryExpr *Binder = dyn_cast<CXXBindTemporaryExpr>(SubExpr)) SubExpr = Binder->getSubExpr(); @@ -1242,7 +1306,7 @@ InitListExpr::InitListExpr(ASTContext &C, SourceLocation lbraceloc, Expr **initExprs, unsigned numInits, SourceLocation rbraceloc) : Expr(InitListExprClass, QualType(), VK_RValue, OK_Ordinary, false, false, - false), + false, false), InitExprs(C, numInits), LBraceLoc(lbraceloc), RBraceLoc(rbraceloc), SyntacticForm(0), HadArrayRangeDesignator(false) @@ -1252,6 +1316,8 @@ InitListExpr::InitListExpr(ASTContext &C, SourceLocation lbraceloc, ExprBits.TypeDependent = true; if (initExprs[I]->isValueDependent()) ExprBits.ValueDependent = true; + if (initExprs[I]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (initExprs[I]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; } @@ -1490,6 +1556,17 @@ bool Expr::isUnusedResultAWarning(SourceLocation &Loc, SourceRange &R1, case ObjCMessageExprClass: { const ObjCMessageExpr *ME = cast<ObjCMessageExpr>(this); + if (Ctx.getLangOptions().ObjCAutoRefCount && + ME->isInstanceMessage() && + !ME->getType()->isVoidType() && + ME->getSelector().getIdentifierInfoForSlot(0) && + ME->getSelector().getIdentifierInfoForSlot(0) + ->getName().startswith("init")) { + Loc = getExprLoc(); + R1 = ME->getSourceRange(); + return true; + } + const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD && MD->getAttr<WarnUnusedResultAttr>()) { Loc = getExprLoc(); @@ -1584,6 +1661,9 @@ bool Expr::isOBJCGCCandidate(ASTContext &Ctx) const { return cast<UnaryOperator>(E)->getSubExpr()->isOBJCGCCandidate(Ctx); case ImplicitCastExprClass: return cast<ImplicitCastExpr>(E)->getSubExpr()->isOBJCGCCandidate(Ctx); + case MaterializeTemporaryExprClass: + return cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr() + ->isOBJCGCCandidate(Ctx); case CStyleCastExprClass: return cast<CStyleCastExpr>(E)->getSubExpr()->isOBJCGCCandidate(Ctx); case DeclRefExprClass: { @@ -1858,7 +1938,8 @@ Expr::CanThrowResult Expr::CanThrow(ASTContext &C) const { case CXXStaticCastExprClass: case CXXFunctionalCastExprClass: case BinaryOperatorClass: - case CompoundAssignOperatorClass: { + case CompoundAssignOperatorClass: + case MaterializeTemporaryExprClass: { CanThrowResult CT = isTypeDependent() ? CT_Dependent : CT_Cannot; return MergeCanThrow(CT, CanSubExprsThrow(C, this)); } @@ -1938,6 +2019,12 @@ Expr *Expr::IgnoreParenCasts() { continue; } } + if (MaterializeTemporaryExpr *Materialize + = dyn_cast<MaterializeTemporaryExpr>(E)) { + E = Materialize->GetTemporaryExpr(); + continue; + } + return E; } } @@ -1967,6 +2054,10 @@ Expr *Expr::IgnoreParenLValueCasts() { E = P->getResultExpr(); continue; } + } else if (MaterializeTemporaryExpr *Materialize + = dyn_cast<MaterializeTemporaryExpr>(E)) { + E = Materialize->GetTemporaryExpr(); + continue; } break; } @@ -1996,13 +2087,18 @@ Expr *Expr::IgnoreParenImpCasts() { continue; } } + if (MaterializeTemporaryExpr *Materialize + = dyn_cast<MaterializeTemporaryExpr>(E)) { + E = Materialize->GetTemporaryExpr(); + continue; + } return E; } } Expr *Expr::IgnoreConversionOperator() { if (CXXMemberCallExpr *MCE = dyn_cast<CXXMemberCallExpr>(this)) { - if (isa<CXXConversionDecl>(MCE->getMethodDecl())) + if (MCE->getMethodDecl() && isa<CXXConversionDecl>(MCE->getMethodDecl())) return MCE->getImplicitObjectArgument(); } return this; @@ -2059,6 +2155,9 @@ Expr *Expr::IgnoreParenNoopCasts(ASTContext &Ctx) { bool Expr::isDefaultArgument() const { const Expr *E = this; + if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E)) + E = M->GetTemporaryExpr(); + while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) E = ICE->getSubExprAsWritten(); @@ -2068,6 +2167,9 @@ bool Expr::isDefaultArgument() const { /// \brief Skip over any no-op casts and any temporary-binding /// expressions. static const Expr *skipTemporaryBindingsNoOpCastsAndParens(const Expr *E) { + if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E)) + E = M->GetTemporaryExpr(); + while (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) { if (ICE->getCastKind() == CK_NoOp) E = ICE->getSubExpr(); @@ -2155,6 +2257,12 @@ bool Expr::isImplicitCXXThis() const { } } + if (const MaterializeTemporaryExpr *M + = dyn_cast<MaterializeTemporaryExpr>(E)) { + E = M->GetTemporaryExpr(); + continue; + } + break; } @@ -2287,6 +2395,10 @@ bool Expr::isConstantInitializer(ASTContext &Ctx, bool IsForRef) const { ->isConstantInitializer(Ctx, false); break; + + case MaterializeTemporaryExprClass: + return llvm::cast<MaterializeTemporaryExpr>(this)->GetTemporaryExpr() + ->isConstantInitializer(Ctx, false); } return isEvaluatable(Ctx); } @@ -2345,6 +2457,9 @@ Expr::isNullPointerConstant(ASTContext &Ctx, } else if (isa<GNUNullExpr>(this)) { // The GNU __null extension is always a null pointer constant. return NPCK_GNUNull; + } else if (const MaterializeTemporaryExpr *M + = dyn_cast<MaterializeTemporaryExpr>(this)) { + return M->GetTemporaryExpr()->isNullPointerConstant(Ctx, NPC); } // C++0x nullptr_t is always a null pointer constant. @@ -2414,10 +2529,14 @@ FieldDecl *Expr::getBitField() { if (Field->isBitField()) return Field; - if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E)) + if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(E)) { if (BinOp->isAssignmentOp() && BinOp->getLHS()) return BinOp->getLHS()->getBitField(); + if (BinOp->getOpcode() == BO_Comma && BinOp->getRHS()) + return BinOp->getRHS()->getBitField(); + } + return 0; } @@ -2517,9 +2636,10 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, SourceLocation RBracLoc) : Expr(ObjCMessageExprClass, T, VK, OK_Ordinary, /*TypeDependent=*/false, /*ValueDependent=*/false, + /*InstantiationDependent=*/false, /*ContainsUnexpandedParameterPack=*/false), NumArgs(NumArgs), Kind(IsInstanceSuper? SuperInstance : SuperClass), - HasMethod(Method != 0), SuperLoc(SuperLoc), + HasMethod(Method != 0), IsDelegateInitCall(false), SuperLoc(SuperLoc), SelectorOrMethod(reinterpret_cast<uintptr_t>(Method? Method : Sel.getAsOpaquePtr())), SelectorLoc(SelLoc), LBracLoc(LBracLoc), RBracLoc(RBracLoc) @@ -2539,8 +2659,10 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, Expr **Args, unsigned NumArgs, SourceLocation RBracLoc) : Expr(ObjCMessageExprClass, T, VK, OK_Ordinary, T->isDependentType(), - T->isDependentType(), T->containsUnexpandedParameterPack()), - NumArgs(NumArgs), Kind(Class), HasMethod(Method != 0), + T->isDependentType(), T->isInstantiationDependentType(), + T->containsUnexpandedParameterPack()), + NumArgs(NumArgs), Kind(Class), + HasMethod(Method != 0), IsDelegateInitCall(false), SelectorOrMethod(reinterpret_cast<uintptr_t>(Method? Method : Sel.getAsOpaquePtr())), SelectorLoc(SelLoc), LBracLoc(LBracLoc), RBracLoc(RBracLoc) @@ -2552,6 +2674,8 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, ExprBits.TypeDependent = true; if (Args[I]->isValueDependent()) ExprBits.ValueDependent = true; + if (Args[I]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (Args[I]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -2570,8 +2694,10 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, SourceLocation RBracLoc) : Expr(ObjCMessageExprClass, T, VK, OK_Ordinary, Receiver->isTypeDependent(), Receiver->isTypeDependent(), + Receiver->isInstantiationDependent(), Receiver->containsUnexpandedParameterPack()), - NumArgs(NumArgs), Kind(Instance), HasMethod(Method != 0), + NumArgs(NumArgs), Kind(Instance), + HasMethod(Method != 0), IsDelegateInitCall(false), SelectorOrMethod(reinterpret_cast<uintptr_t>(Method? Method : Sel.getAsOpaquePtr())), SelectorLoc(SelLoc), LBracLoc(LBracLoc), RBracLoc(RBracLoc) @@ -2583,6 +2709,8 @@ ObjCMessageExpr::ObjCMessageExpr(QualType T, ExprBits.TypeDependent = true; if (Args[I]->isValueDependent()) ExprBits.ValueDependent = true; + if (Args[I]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (Args[I]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -2702,6 +2830,19 @@ ObjCInterfaceDecl *ObjCMessageExpr::getReceiverInterface() const { return 0; } +llvm::StringRef ObjCBridgedCastExpr::getBridgeKindName() const { + switch (getBridgeKind()) { + case OBC_Bridge: + return "__bridge"; + case OBC_BridgeTransfer: + return "__bridge_transfer"; + case OBC_BridgeRetained: + return "__bridge_retained"; + } + + return "__bridge"; +} + bool ChooseExpr::isConditionTrue(const ASTContext &C) const { return getCond()->EvaluateAsInt(C) != 0; } @@ -2711,6 +2852,7 @@ ShuffleVectorExpr::ShuffleVectorExpr(ASTContext &C, Expr **args, unsigned nexpr, SourceLocation RP) : Expr(ShuffleVectorExprClass, Type, VK_RValue, OK_Ordinary, Type->isDependentType(), Type->isDependentType(), + Type->isInstantiationDependentType(), Type->containsUnexpandedParameterPack()), BuiltinLoc(BLoc), RParenLoc(RP), NumExprs(nexpr) { @@ -2720,6 +2862,8 @@ ShuffleVectorExpr::ShuffleVectorExpr(ASTContext &C, Expr **args, unsigned nexpr, ExprBits.TypeDependent = true; if (args[i]->isValueDependent()) ExprBits.ValueDependent = true; + if (args[i]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (args[i]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -2749,6 +2893,7 @@ GenericSelectionExpr::GenericSelectionExpr(ASTContext &Context, AssocExprs[ResultIndex]->getObjectKind(), AssocExprs[ResultIndex]->isTypeDependent(), AssocExprs[ResultIndex]->isValueDependent(), + AssocExprs[ResultIndex]->isInstantiationDependent(), ContainsUnexpandedParameterPack), AssocTypes(new (Context) TypeSourceInfo*[NumAssocs]), SubExprs(new (Context) Stmt*[END_EXPR+NumAssocs]), NumAssocs(NumAssocs), @@ -2769,8 +2914,9 @@ GenericSelectionExpr::GenericSelectionExpr(ASTContext &Context, Context.DependentTy, VK_RValue, OK_Ordinary, - /*isTypeDependent=*/ true, - /*isValueDependent=*/ true, + /*isTypeDependent=*/true, + /*isValueDependent=*/true, + /*isInstantiationDependent=*/true, ContainsUnexpandedParameterPack), AssocTypes(new (Context) TypeSourceInfo*[NumAssocs]), SubExprs(new (Context) Stmt*[END_EXPR+NumAssocs]), NumAssocs(NumAssocs), @@ -2785,7 +2931,7 @@ GenericSelectionExpr::GenericSelectionExpr(ASTContext &Context, // DesignatedInitExpr //===----------------------------------------------------------------------===// -IdentifierInfo *DesignatedInitExpr::Designator::getFieldName() { +IdentifierInfo *DesignatedInitExpr::Designator::getFieldName() const { assert(Kind == FieldDesignator && "Only valid on a field designator"); if (Field.NameOrField & 0x01) return reinterpret_cast<IdentifierInfo *>(Field.NameOrField&~0x01); @@ -2804,6 +2950,7 @@ DesignatedInitExpr::DesignatedInitExpr(ASTContext &C, QualType Ty, : Expr(DesignatedInitExprClass, Ty, Init->getValueKind(), Init->getObjectKind(), Init->isTypeDependent(), Init->isValueDependent(), + Init->isInstantiationDependent(), Init->containsUnexpandedParameterPack()), EqualOrColonLoc(EqualOrColonLoc), GNUSyntax(GNUSyntax), NumDesignators(NumDesignators), NumSubExprs(NumIndexExprs + 1) { @@ -2824,7 +2971,8 @@ DesignatedInitExpr::DesignatedInitExpr(ASTContext &C, QualType Ty, Expr *Index = IndexExprs[IndexIdx]; if (Index->isTypeDependent() || Index->isValueDependent()) ExprBits.ValueDependent = true; - + if (Index->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; // Propagate unexpanded parameter packs. if (Index->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -2836,9 +2984,14 @@ DesignatedInitExpr::DesignatedInitExpr(ASTContext &C, QualType Ty, Expr *Start = IndexExprs[IndexIdx]; Expr *End = IndexExprs[IndexIdx + 1]; if (Start->isTypeDependent() || Start->isValueDependent() || - End->isTypeDependent() || End->isValueDependent()) + End->isTypeDependent() || End->isValueDependent()) { ExprBits.ValueDependent = true; - + ExprBits.InstantiationDependent = true; + } else if (Start->isInstantiationDependent() || + End->isInstantiationDependent()) { + ExprBits.InstantiationDependent = true; + } + // Propagate unexpanded parameter packs. if (Start->containsUnexpandedParameterPack() || End->containsUnexpandedParameterPack()) @@ -2960,17 +3113,19 @@ void DesignatedInitExpr::ExpandDesignator(ASTContext &C, unsigned Idx, ParenListExpr::ParenListExpr(ASTContext& C, SourceLocation lparenloc, Expr **exprs, unsigned nexprs, - SourceLocation rparenloc) - : Expr(ParenListExprClass, QualType(), VK_RValue, OK_Ordinary, - false, false, false), + SourceLocation rparenloc, QualType T) + : Expr(ParenListExprClass, T, VK_RValue, OK_Ordinary, + false, false, false, false), NumExprs(nexprs), LParenLoc(lparenloc), RParenLoc(rparenloc) { - + assert(!T.isNull() && "ParenListExpr must have a valid type"); Exprs = new (C) Stmt*[nexprs]; for (unsigned i = 0; i != nexprs; ++i) { if (exprs[i]->isTypeDependent()) ExprBits.TypeDependent = true; if (exprs[i]->isValueDependent()) ExprBits.ValueDependent = true; + if (exprs[i]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (exprs[i]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -2981,6 +3136,8 @@ ParenListExpr::ParenListExpr(ASTContext& C, SourceLocation lparenloc, const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) { if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(e)) e = ewc->getSubExpr(); + if (const MaterializeTemporaryExpr *m = dyn_cast<MaterializeTemporaryExpr>(e)) + e = m->GetTemporaryExpr(); e = cast<CXXConstructExpr>(e)->getArg(0); while (const ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(e)) e = ice->getSubExpr(); @@ -3033,13 +3190,16 @@ Stmt::child_range ObjCMessageExpr::children() { BlockDeclRefExpr::BlockDeclRefExpr(VarDecl *d, QualType t, ExprValueKind VK, SourceLocation l, bool ByRef, bool constAdded) - : Expr(BlockDeclRefExprClass, t, VK, OK_Ordinary, false, false, + : Expr(BlockDeclRefExprClass, t, VK, OK_Ordinary, false, false, false, d->isParameterPack()), D(d), Loc(l), IsByRef(ByRef), ConstQualAdded(constAdded) { bool TypeDependent = false; bool ValueDependent = false; - computeDeclRefDependence(D, getType(), TypeDependent, ValueDependent); + bool InstantiationDependent = false; + computeDeclRefDependence(D, getType(), TypeDependent, ValueDependent, + InstantiationDependent); ExprBits.TypeDependent = TypeDependent; ExprBits.ValueDependent = ValueDependent; + ExprBits.InstantiationDependent = InstantiationDependent; } diff --git a/lib/AST/ExprCXX.cpp b/lib/AST/ExprCXX.cpp index 1a1a0a36a65b..f92afffb5851 100644 --- a/lib/AST/ExprCXX.cpp +++ b/lib/AST/ExprCXX.cpp @@ -57,6 +57,7 @@ CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew, SourceLocation constructorRParen) : Expr(CXXNewExprClass, ty, VK_RValue, OK_Ordinary, ty->isDependentType(), ty->isDependentType(), + ty->isInstantiationDependentType(), ty->containsUnexpandedParameterPack()), GlobalNew(globalNew), Initializer(initializer), UsualArrayDeleteWantsSize(usualArrayDeleteWantsSize), @@ -68,6 +69,9 @@ CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew, AllocateArgsArray(C, arraySize != 0, numPlaceArgs, numConsArgs); unsigned i = 0; if (Array) { + if (arraySize->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; + if (arraySize->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -75,6 +79,8 @@ CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew, } for (unsigned j = 0; j < NumPlacementArgs; ++j) { + if (placementArgs[j]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (placementArgs[j]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -82,6 +88,8 @@ CXXNewExpr::CXXNewExpr(ASTContext &C, bool globalNew, FunctionDecl *operatorNew, } for (unsigned j = 0; j < NumConstructorArgs; ++j) { + if (constructorArgs[j]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (constructorArgs[j]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -144,6 +152,14 @@ CXXPseudoDestructorExpr::CXXPseudoDestructorExpr(ASTContext &Context, (DestroyedType.getTypeSourceInfo() && DestroyedType.getTypeSourceInfo()->getType()->isDependentType())), /*isValueDependent=*/Base->isValueDependent(), + (Base->isInstantiationDependent() || + (QualifierLoc && + QualifierLoc.getNestedNameSpecifier()->isInstantiationDependent()) || + (ScopeType && + ScopeType->getType()->isInstantiationDependentType()) || + (DestroyedType.getTypeSourceInfo() && + DestroyedType.getTypeSourceInfo()->getType() + ->isInstantiationDependentType())), // ContainsUnexpandedParameterPack (Base->containsUnexpandedParameterPack() || (QualifierLoc && @@ -212,9 +228,14 @@ OverloadExpr::OverloadExpr(StmtClass K, ASTContext &C, UnresolvedSetIterator Begin, UnresolvedSetIterator End, bool KnownDependent, + bool KnownInstantiationDependent, bool KnownContainsUnexpandedParameterPack) : Expr(K, C.OverloadTy, VK_LValue, OK_Ordinary, KnownDependent, KnownDependent, + (KnownInstantiationDependent || + NameInfo.isInstantiationDependent() || + (QualifierLoc && + QualifierLoc.getNestedNameSpecifier()->isInstantiationDependent())), (KnownContainsUnexpandedParameterPack || NameInfo.containsUnexpandedParameterPack() || (QualifierLoc && @@ -246,14 +267,18 @@ OverloadExpr::OverloadExpr(StmtClass K, ASTContext &C, // expansions. if (TemplateArgs) { bool Dependent = false; + bool InstantiationDependent = false; bool ContainsUnexpandedParameterPack = false; getExplicitTemplateArgs().initializeFrom(*TemplateArgs, Dependent, + InstantiationDependent, ContainsUnexpandedParameterPack); if (Dependent) { - ExprBits.TypeDependent = true; - ExprBits.ValueDependent = true; - } + ExprBits.TypeDependent = true; + ExprBits.ValueDependent = true; + } + if (InstantiationDependent) + ExprBits.InstantiationDependent = true; if (ContainsUnexpandedParameterPack) ExprBits.ContainsUnexpandedParameterPack = true; } @@ -291,6 +316,9 @@ DependentScopeDeclRefExpr::DependentScopeDeclRefExpr(QualType T, const TemplateArgumentListInfo *Args) : Expr(DependentScopeDeclRefExprClass, T, VK_LValue, OK_Ordinary, true, true, + (NameInfo.isInstantiationDependent() || + (QualifierLoc && + QualifierLoc.getNestedNameSpecifier()->isInstantiationDependent())), (NameInfo.containsUnexpandedParameterPack() || (QualifierLoc && QualifierLoc.getNestedNameSpecifier() @@ -300,11 +328,14 @@ DependentScopeDeclRefExpr::DependentScopeDeclRefExpr(QualType T, { if (Args) { bool Dependent = true; + bool InstantiationDependent = true; bool ContainsUnexpandedParameterPack = ExprBits.ContainsUnexpandedParameterPack; reinterpret_cast<ExplicitTemplateArgumentList*>(this+1) - ->initializeFrom(*Args, Dependent, ContainsUnexpandedParameterPack); + ->initializeFrom(*Args, Dependent, InstantiationDependent, + ContainsUnexpandedParameterPack); + ExprBits.ContainsUnexpandedParameterPack = ContainsUnexpandedParameterPack; } } @@ -632,6 +663,7 @@ CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T, SourceRange ParenRange) : Expr(SC, T, VK_RValue, OK_Ordinary, T->isDependentType(), T->isDependentType(), + T->isInstantiationDependentType(), T->containsUnexpandedParameterPack()), Constructor(D), Loc(Loc), ParenRange(ParenRange), Elidable(elidable), ZeroInitialization(ZeroInitialization), ConstructKind(ConstructKind), @@ -645,6 +677,8 @@ CXXConstructExpr::CXXConstructExpr(ASTContext &C, StmtClass SC, QualType T, if (args[i]->isValueDependent()) ExprBits.ValueDependent = true; + if (args[i]->isInstantiationDependent()) + ExprBits.InstantiationDependent = true; if (args[i]->containsUnexpandedParameterPack()) ExprBits.ContainsUnexpandedParameterPack = true; @@ -660,6 +694,7 @@ ExprWithCleanups::ExprWithCleanups(ASTContext &C, : Expr(ExprWithCleanupsClass, subexpr->getType(), subexpr->getValueKind(), subexpr->getObjectKind(), subexpr->isTypeDependent(), subexpr->isValueDependent(), + subexpr->isInstantiationDependent(), subexpr->containsUnexpandedParameterPack()), SubExpr(subexpr), Temps(0), NumTemps(0) { if (numtemps) { @@ -690,8 +725,11 @@ CXXUnresolvedConstructExpr::CXXUnresolvedConstructExpr(TypeSourceInfo *Type, SourceLocation RParenLoc) : Expr(CXXUnresolvedConstructExprClass, Type->getType().getNonReferenceType(), - VK_LValue, OK_Ordinary, - Type->getType()->isDependentType(), true, + (Type->getType()->isLValueReferenceType() ? VK_LValue + :Type->getType()->isRValueReferenceType()? VK_XValue + :VK_RValue), + OK_Ordinary, + Type->getType()->isDependentType(), true, true, Type->getType()->containsUnexpandedParameterPack()), Type(Type), LParenLoc(LParenLoc), @@ -740,7 +778,7 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(ASTContext &C, DeclarationNameInfo MemberNameInfo, const TemplateArgumentListInfo *TemplateArgs) : Expr(CXXDependentScopeMemberExprClass, C.DependentTy, - VK_LValue, OK_Ordinary, true, true, + VK_LValue, OK_Ordinary, true, true, true, ((Base && Base->containsUnexpandedParameterPack()) || (QualifierLoc && QualifierLoc.getNestedNameSpecifier() @@ -753,8 +791,10 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(ASTContext &C, MemberNameInfo(MemberNameInfo) { if (TemplateArgs) { bool Dependent = true; + bool InstantiationDependent = true; bool ContainsUnexpandedParameterPack = false; getExplicitTemplateArgs().initializeFrom(*TemplateArgs, Dependent, + InstantiationDependent, ContainsUnexpandedParameterPack); if (ContainsUnexpandedParameterPack) ExprBits.ContainsUnexpandedParameterPack = true; @@ -769,7 +809,7 @@ CXXDependentScopeMemberExpr::CXXDependentScopeMemberExpr(ASTContext &C, NamedDecl *FirstQualifierFoundInScope, DeclarationNameInfo MemberNameInfo) : Expr(CXXDependentScopeMemberExprClass, C.DependentTy, - VK_LValue, OK_Ordinary, true, true, + VK_LValue, OK_Ordinary, true, true, true, ((Base && Base->containsUnexpandedParameterPack()) || (QualifierLoc && QualifierLoc.getNestedNameSpecifier()-> @@ -874,6 +914,8 @@ UnresolvedMemberExpr::UnresolvedMemberExpr(ASTContext &C, // Dependent ((Base && Base->isTypeDependent()) || BaseType->isDependentType()), + ((Base && Base->isInstantiationDependent()) || + BaseType->isInstantiationDependentType()), // Contains unexpanded parameter pack ((Base && Base->containsUnexpandedParameterPack()) || BaseType->containsUnexpandedParameterPack())), @@ -962,7 +1004,7 @@ SubstNonTypeTemplateParmPackExpr(QualType T, SourceLocation NameLoc, const TemplateArgument &ArgPack) : Expr(SubstNonTypeTemplateParmPackExprClass, T, VK_RValue, OK_Ordinary, - true, false, true), + true, true, true, true), Param(Param), Arguments(ArgPack.pack_begin()), NumArguments(ArgPack.pack_size()), NameLoc(NameLoc) { } diff --git a/lib/AST/ExprClassification.cpp b/lib/AST/ExprClassification.cpp index d177cb5cbc97..e7888a6aa7b3 100644 --- a/lib/AST/ExprClassification.cpp +++ b/lib/AST/ExprClassification.cpp @@ -117,7 +117,6 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::UnresolvedLookupExprClass: case Expr::UnresolvedMemberExprClass: case Expr::CXXDependentScopeMemberExprClass: - case Expr::CXXUnresolvedConstructExprClass: case Expr::DependentScopeDeclRefExprClass: // ObjC instance variables are lvalues // FIXME: ObjC++0x might have different rules @@ -162,9 +161,13 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::SizeOfPackExprClass: case Expr::SubstNonTypeTemplateParmPackExprClass: case Expr::AsTypeExprClass: + case Expr::ObjCIndirectCopyRestoreExprClass: return Cl::CL_PRValue; // Next come the complicated cases. + case Expr::SubstNonTypeTemplateParmExprClass: + return ClassifyInternal(Ctx, + cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement()); // C++ [expr.sub]p1: The result is an lvalue of type "T". // However, subscripting vector types is more like member access. @@ -289,10 +292,15 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::CXXDynamicCastExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::CXXConstCastExprClass: + case Expr::ObjCBridgedCastExprClass: // Only in C++ can casts be interesting at all. if (!Lang.CPlusPlus) return Cl::CL_PRValue; return ClassifyUnnamed(Ctx, cast<ExplicitCastExpr>(E)->getTypeAsWritten()); + case Expr::CXXUnresolvedConstructExprClass: + return ClassifyUnnamed(Ctx, + cast<CXXUnresolvedConstructExpr>(E)->getTypeAsWritten()); + case Expr::BinaryConditionalOperatorClass: { if (!Lang.CPlusPlus) return Cl::CL_PRValue; const BinaryConditionalOperator *co = cast<BinaryConditionalOperator>(E); @@ -339,6 +347,11 @@ static Cl::Kinds ClassifyInternal(ASTContext &Ctx, const Expr *E) { case Expr::PackExpansionExprClass: return ClassifyInternal(Ctx, cast<PackExpansionExpr>(E)->getPattern()); + + case Expr::MaterializeTemporaryExprClass: + return cast<MaterializeTemporaryExpr>(E)->isBoundToLvalueReference() + ? Cl::CL_LValue + : Cl::CL_XValue; } llvm_unreachable("unhandled expression kind in classification"); diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 06c5645afb3f..786155af281d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -224,11 +224,10 @@ static APSInt HandleFloatToIntCast(QualType DestType, QualType SrcType, bool DestSigned = DestType->isSignedIntegerOrEnumerationType(); // FIXME: Warning for overflow. - uint64_t Space[4]; + APSInt Result(DestWidth, !DestSigned); bool ignored; - (void)Value.convertToInteger(Space, DestWidth, DestSigned, - llvm::APFloat::rmTowardZero, &ignored); - return APSInt(llvm::APInt(DestWidth, 4, Space), !DestSigned); + (void)Value.convertToInteger(Result, llvm::APFloat::rmTowardZero, &ignored); + return Result; } static APFloat HandleFloatToFloatCast(QualType DestType, QualType SrcType, @@ -282,6 +281,17 @@ public: return true; return false; } + bool VisitObjCIvarRefExpr(const ObjCIvarRefExpr *E) { + if (Info.Ctx.getCanonicalType(E->getType()).isVolatileQualified()) + return true; + return false; + } + bool VisitBlockDeclRefExpr (const BlockDeclRefExpr *E) { + if (Info.Ctx.getCanonicalType(E->getType()).isVolatileQualified()) + return true; + return false; + } + // We don't want to evaluate BlockExprs multiple times, as they generate // a ton of code. bool VisitBlockExpr(const BlockExpr *E) { return true; } @@ -395,6 +405,8 @@ public: { return StmtVisitorTy::Visit(E->getChosenSubExpr(Info.Ctx)); } RetTy VisitGenericSelectionExpr(const GenericSelectionExpr *E) { return StmtVisitorTy::Visit(E->getResultExpr()); } + RetTy VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E) + { return StmtVisitorTy::Visit(E->getReplacement()); } RetTy VisitBinaryConditionalOperator(const BinaryConditionalOperator *E) { OpaqueValueEvaluation opaque(Info, E->getOpaqueValue(), E->getCommon()); @@ -525,15 +537,7 @@ bool LValueExprEvaluator::VisitMemberExpr(const MemberExpr *E) { if (FD->getType()->isReferenceType()) return false; - // FIXME: This is linear time. - unsigned i = 0; - for (RecordDecl::field_iterator Field = RD->field_begin(), - FieldEnd = RD->field_end(); - Field != FieldEnd; (void)++Field, ++i) { - if (*Field == FD) - break; - } - + unsigned i = FD->getFieldIndex(); Result.Offset += Info.Ctx.toCharUnitsFromBits(RL.getFieldOffset(i)); return true; } @@ -945,7 +949,7 @@ public: : ExprEvaluatorBaseTy(info), Result(result) {} bool Success(const llvm::APSInt &SI, const Expr *E) { - assert(E->getType()->isIntegralOrEnumerationType() && + assert(E->getType()->isIntegralOrEnumerationType() && "Invalid evaluation result."); assert(SI.isSigned() == E->getType()->isSignedIntegerOrEnumerationType() && "Invalid evaluation result."); @@ -1095,8 +1099,25 @@ static bool EvaluateInteger(const Expr* E, APSInt &Result, EvalInfo &Info) { bool IntExprEvaluator::CheckReferencedDecl(const Expr* E, const Decl* D) { // Enums are integer constant exprs. - if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) - return Success(ECD->getInitVal(), E); + if (const EnumConstantDecl *ECD = dyn_cast<EnumConstantDecl>(D)) { + // Check for signedness/width mismatches between E type and ECD value. + bool SameSign = (ECD->getInitVal().isSigned() + == E->getType()->isSignedIntegerOrEnumerationType()); + bool SameWidth = (ECD->getInitVal().getBitWidth() + == Info.Ctx.getIntWidth(E->getType())); + if (SameSign && SameWidth) + return Success(ECD->getInitVal(), E); + else { + // Get rid of mismatch (otherwise Success assertions will fail) + // by computing a new value matching the type of E. + llvm::APSInt Val = ECD->getInitVal(); + if (!SameSign) + Val.setIsSigned(!ECD->getInitVal().isSigned()); + if (!SameWidth) + Val = Val.extOrTrunc(Info.Ctx.getIntWidth(E->getType())); + return Success(Val, E); + } + } // In C++, const, non-volatile integers initialized with ICEs are ICEs. // In C, they can also be folded, although they are not ICEs. @@ -1797,6 +1818,9 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_GetObjCProperty: case CK_LValueBitCast: case CK_UserDefinedConversion: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: return false; case CK_LValueToRValue: @@ -2301,6 +2325,9 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { case CK_FloatingComplexToBoolean: case CK_IntegralComplexToReal: case CK_IntegralComplexToBoolean: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: llvm_unreachable("invalid cast kind for complex value"); case CK_LValueToRValue: @@ -2771,6 +2798,8 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::PackExpansionExprClass: case Expr::SubstNonTypeTemplateParmPackExprClass: case Expr::AsTypeExprClass: + case Expr::ObjCIndirectCopyRestoreExprClass: + case Expr::MaterializeTemporaryExprClass: return ICEDiag(2, E->getLocStart()); case Expr::SizeOfPackExprClass: @@ -2778,6 +2807,10 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { // GCC considers the GNU __null value to be an integral constant expression. return NoDiag(); + case Expr::SubstNonTypeTemplateParmExprClass: + return + CheckICE(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(), Ctx); + case Expr::ParenExprClass: return CheckICE(cast<ParenExpr>(E)->getSubExpr(), Ctx); case Expr::GenericSelectionExprClass: @@ -2995,7 +3028,8 @@ static ICEDiag CheckICE(const Expr* E, ASTContext &Ctx) { case Expr::CXXFunctionalCastExprClass: case Expr::CXXStaticCastExprClass: case Expr::CXXReinterpretCastExprClass: - case Expr::CXXConstCastExprClass: { + case Expr::CXXConstCastExprClass: + case Expr::ObjCBridgedCastExprClass: { const Expr *SubExpr = cast<CastExpr>(E)->getSubExpr(); if (SubExpr->getType()->isIntegralOrEnumerationType()) return CheckICE(SubExpr, Ctx); diff --git a/lib/AST/ExternalASTSource.cpp b/lib/AST/ExternalASTSource.cpp index f428318a21e3..b96d65a729ca 100644 --- a/lib/AST/ExternalASTSource.cpp +++ b/lib/AST/ExternalASTSource.cpp @@ -51,11 +51,11 @@ ExternalASTSource::FindExternalVisibleDeclsByName(const DeclContext *DC, void ExternalASTSource::MaterializeVisibleDecls(const DeclContext *DC) { } -bool +ExternalLoadResult ExternalASTSource::FindExternalLexicalDecls(const DeclContext *DC, bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl<Decl*> &Result) { - return true; + return ELR_AlreadyLoaded; } void ExternalASTSource::getMemoryBufferSizes(MemoryBufferSizes &sizes) const { } diff --git a/lib/AST/ItaniumMangle.cpp b/lib/AST/ItaniumMangle.cpp index e81ec7e54b62..ec9863b298e6 100644 --- a/lib/AST/ItaniumMangle.cpp +++ b/lib/AST/ItaniumMangle.cpp @@ -21,6 +21,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/ABI.h" #include "clang/Basic/SourceManager.h" @@ -236,6 +237,9 @@ private: bool mangleSubstitution(TemplateName Template); bool mangleSubstitution(uintptr_t Ptr); + void mangleExistingSubstitution(QualType type); + void mangleExistingSubstitution(TemplateName name); + bool mangleStandardSubstitution(const NamedDecl *ND); void addSubstitution(const NamedDecl *ND) { @@ -255,9 +259,6 @@ private: DeclarationName name, unsigned KnownArity = UnknownArity); - static bool isUnresolvedType(const Type *type); - void mangleUnresolvedType(const Type *type); - void mangleName(const TemplateDecl *TD, const TemplateArgument *TemplateArgs, unsigned NumTemplateArgs); @@ -318,7 +319,7 @@ private: unsigned NumTemplateArgs); void mangleTemplateArgs(const TemplateParameterList &PL, const TemplateArgumentList &AL); - void mangleTemplateArg(const NamedDecl *P, const TemplateArgument &A); + void mangleTemplateArg(const NamedDecl *P, TemplateArgument A); void mangleUnresolvedTemplateArgs(const TemplateArgument *args, unsigned numArgs); @@ -451,13 +452,8 @@ void CXXNameMangler::mangleFunctionEncoding(const FunctionDecl *FD) { FD = PrimaryTemplate->getTemplatedDecl(); } - // Do the canonicalization out here because parameter types can - // undergo additional canonicalization (e.g. array decay). - const FunctionType *FT - = cast<FunctionType>(Context.getASTContext() - .getCanonicalType(FD->getType())); - - mangleBareFunctionType(FT, MangleReturnType); + mangleBareFunctionType(FD->getType()->getAs<FunctionType>(), + MangleReturnType); } static const DeclContext *IgnoreLinkageSpecDecls(const DeclContext *DC) { @@ -597,19 +593,13 @@ void CXXNameMangler::mangleUnscopedTemplateName(TemplateName Template) { if (mangleSubstitution(Template)) return; - // FIXME: How to cope with operators here? DependentTemplateName *Dependent = Template.getAsDependentTemplateName(); assert(Dependent && "Not a dependent template name?"); - if (!Dependent->isIdentifier()) { - // FIXME: We can't possibly know the arity of the operator here! - Diagnostic &Diags = Context.getDiags(); - unsigned DiagID = Diags.getCustomDiagID(Diagnostic::Error, - "cannot mangle dependent operator name"); - Diags.Report(DiagID); - return; - } + if (const IdentifierInfo *Id = Dependent->getIdentifier()) + mangleSourceName(Id); + else + mangleOperatorName(Dependent->getOperator(), UnknownArity); - mangleSourceName(Dependent->getIdentifier()); addSubstitution(Template); } @@ -702,31 +692,6 @@ void CXXNameMangler::manglePrefix(QualType type) { } } -/// Returns true if the given type, appearing within an -/// unresolved-name, should be mangled as an unresolved-type. -bool CXXNameMangler::isUnresolvedType(const Type *type) { - // <unresolved-type> ::= <template-param> - // ::= <decltype> - // ::= <template-template-param> <template-args> - // (this last is not official yet) - - if (isa<TemplateTypeParmType>(type)) return true; - if (isa<DecltypeType>(type)) return true; - // typeof? - if (const TemplateSpecializationType *tst = - dyn_cast<TemplateSpecializationType>(type)) { - TemplateDecl *temp = tst->getTemplateName().getAsTemplateDecl(); - if (temp && isa<TemplateTemplateParmDecl>(temp)) - return true; - } - return false; -} - -void CXXNameMangler::mangleUnresolvedType(const Type *type) { - // This seems to be do everything we want. - mangleType(QualType(type, 0)); -} - /// Mangle everything prior to the base-unresolved-name in an unresolved-name. /// /// \param firstQualifierLookup - the entity found by unqualified lookup @@ -794,45 +759,141 @@ void CXXNameMangler::mangleUnresolvedPrefix(NestedNameSpecifier *qualifier, } else { // Otherwise, all the cases want this. Out << "sr"; + } + + // Only certain other types are valid as prefixes; enumerate them. + switch (type->getTypeClass()) { + case Type::Builtin: + case Type::Complex: + case Type::Pointer: + case Type::BlockPointer: + case Type::LValueReference: + case Type::RValueReference: + case Type::MemberPointer: + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::VariableArray: + case Type::DependentSizedArray: + case Type::DependentSizedExtVector: + case Type::Vector: + case Type::ExtVector: + case Type::FunctionProto: + case Type::FunctionNoProto: + case Type::Enum: + case Type::Paren: + case Type::Elaborated: + case Type::Attributed: + case Type::Auto: + case Type::PackExpansion: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + llvm_unreachable("type is illegal as a nested name specifier"); + + case Type::SubstTemplateTypeParmPack: + // FIXME: not clear how to mangle this! + // template <class T...> class A { + // template <class U...> void foo(decltype(T::foo(U())) x...); + // }; + Out << "_SUBSTPACK_"; + break; + + // <unresolved-type> ::= <template-param> + // ::= <decltype> + // ::= <template-template-param> <template-args> + // (this last is not official yet) + case Type::TypeOfExpr: + case Type::TypeOf: + case Type::Decltype: + case Type::TemplateTypeParm: + case Type::UnaryTransform: + case Type::SubstTemplateTypeParm: + unresolvedType: + assert(!qualifier->getPrefix()); + + // We only get here recursively if we're followed by identifiers. + if (recursive) Out << 'N'; + + // This seems to do everything we want. It's not really + // sanctioned for a substituted template parameter, though. + mangleType(QualType(type, 0)); + + // We never want to print 'E' directly after an unresolved-type, + // so we return directly. + return; + + case Type::Typedef: + mangleSourceName(cast<TypedefType>(type)->getDecl()->getIdentifier()); + break; + + case Type::UnresolvedUsing: + mangleSourceName(cast<UnresolvedUsingType>(type)->getDecl() + ->getIdentifier()); + break; - if (isUnresolvedType(type)) { - // We only get here recursively if we're followed by identifiers. - if (recursive) Out << 'N'; - mangleUnresolvedType(type); + case Type::Record: + mangleSourceName(cast<RecordType>(type)->getDecl()->getIdentifier()); + break; - // We never want to print 'E' directly after an unresolved-type, - // so we return directly. - return; + case Type::TemplateSpecialization: { + const TemplateSpecializationType *tst + = cast<TemplateSpecializationType>(type); + TemplateName name = tst->getTemplateName(); + switch (name.getKind()) { + case TemplateName::Template: + case TemplateName::QualifiedTemplate: { + TemplateDecl *temp = name.getAsTemplateDecl(); + + // If the base is a template template parameter, this is an + // unresolved type. + assert(temp && "no template for template specialization type"); + if (isa<TemplateTemplateParmDecl>(temp)) goto unresolvedType; + + mangleSourceName(temp->getIdentifier()); + break; } - } - assert(!isUnresolvedType(type)); + case TemplateName::OverloadedTemplate: + case TemplateName::DependentTemplate: + llvm_unreachable("invalid base for a template specialization type"); + + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *subst + = name.getAsSubstTemplateTemplateParm(); + mangleExistingSubstitution(subst->getReplacement()); + break; + } + + case TemplateName::SubstTemplateTemplateParmPack: { + // FIXME: not clear how to mangle this! + // template <template <class U> class T...> class A { + // template <class U...> void foo(decltype(T<U>::foo) x...); + // }; + Out << "_SUBSTPACK_"; + break; + } + } - // Only certain other types are valid as prefixes; enumerate them. - // FIXME: can we get ElaboratedTypes here? - // FIXME: SubstTemplateTypeParmType? - if (const TagType *t = dyn_cast<TagType>(type)) { - mangleSourceName(t->getDecl()->getIdentifier()); - } else if (const TypedefType *t = dyn_cast<TypedefType>(type)) { - mangleSourceName(t->getDecl()->getIdentifier()); - } else if (const UnresolvedUsingType *t - = dyn_cast<UnresolvedUsingType>(type)) { - mangleSourceName(t->getDecl()->getIdentifier()); - } else if (const DependentNameType *t - = dyn_cast<DependentNameType>(type)) { - mangleSourceName(t->getIdentifier()); - } else if (const TemplateSpecializationType *tst - = dyn_cast<TemplateSpecializationType>(type)) { - TemplateDecl *temp = tst->getTemplateName().getAsTemplateDecl(); - assert(temp && "no template for template specialization type"); - mangleSourceName(temp->getIdentifier()); mangleUnresolvedTemplateArgs(tst->getArgs(), tst->getNumArgs()); - } else if (const DependentTemplateSpecializationType *tst - = dyn_cast<DependentTemplateSpecializationType>(type)) { + break; + } + + case Type::InjectedClassName: + mangleSourceName(cast<InjectedClassNameType>(type)->getDecl() + ->getIdentifier()); + break; + + case Type::DependentName: + mangleSourceName(cast<DependentNameType>(type)->getIdentifier()); + break; + + case Type::DependentTemplateSpecialization: { + const DependentTemplateSpecializationType *tst + = cast<DependentTemplateSpecializationType>(type); mangleSourceName(tst->getIdentifier()); mangleUnresolvedTemplateArgs(tst->getArgs(), tst->getNumArgs()); - } else { - llvm_unreachable("unexpected type in nested name specifier!"); + break; + } } break; } @@ -1036,7 +1097,7 @@ void CXXNameMangler::mangleUnqualifiedName(const NamedDecl *ND, case DeclarationName::CXXConversionFunctionName: // <operator-name> ::= cv <type> # (cast) Out << "cv"; - mangleType(Context.getASTContext().getCanonicalType(Name.getCXXNameType())); + mangleType(Name.getCXXNameType()); break; case DeclarationName::CXXOperatorName: { @@ -1323,10 +1384,23 @@ void CXXNameMangler::mangleType(TemplateName TN) { break; } + case TemplateName::SubstTemplateTemplateParm: { + // Substituted template parameters are mangled as the substituted + // template. This will check for the substitution twice, which is + // fine, but we have to return early so that we don't try to *add* + // the substitution twice. + SubstTemplateTemplateParmStorage *subst + = TN.getAsSubstTemplateTemplateParm(); + mangleType(subst->getReplacement()); + return; + } + case TemplateName::SubstTemplateTemplateParmPack: { - SubstTemplateTemplateParmPackStorage *SubstPack - = TN.getAsSubstTemplateTemplateParmPack(); - mangleTemplateParameter(SubstPack->getParameterPack()->getIndex()); + // FIXME: not clear how to mangle this! + // template <template <class> class T...> class A { + // template <template <class> class U...> void foo(B<T,U> x...); + // }; + Out << "_SUBSTPACK_"; break; } } @@ -1464,7 +1538,40 @@ void CXXNameMangler::mangleQualifiers(Qualifiers Quals) { Out << 'U' << ASString.size() << ASString; } - // FIXME: For now, just drop all extension qualifiers on the floor. + llvm::StringRef LifetimeName; + switch (Quals.getObjCLifetime()) { + // Objective-C ARC Extension: + // + // <type> ::= U "__strong" + // <type> ::= U "__weak" + // <type> ::= U "__autoreleasing" + case Qualifiers::OCL_None: + break; + + case Qualifiers::OCL_Weak: + LifetimeName = "__weak"; + break; + + case Qualifiers::OCL_Strong: + LifetimeName = "__strong"; + break; + + case Qualifiers::OCL_Autoreleasing: + LifetimeName = "__autoreleasing"; + break; + + case Qualifiers::OCL_ExplicitNone: + // The __unsafe_unretained qualifier is *not* mangled, so that + // __unsafe_unretained types in ARC produce the same manglings as the + // equivalent (but, naturally, unqualified) types in non-ARC, providing + // better ABI compatibility. + // + // It's safe to do this because unqualified 'id' won't show up + // in any type signatures that need to be mangled. + break; + } + if (!LifetimeName.empty()) + Out << 'U' << LifetimeName.size() << LifetimeName; } void CXXNameMangler::mangleRefQualifier(RefQualifierKind RefQualifier) { @@ -1489,26 +1596,59 @@ void CXXNameMangler::mangleObjCMethodName(const ObjCMethodDecl *MD) { Context.mangleObjCMethodName(MD, Out); } -void CXXNameMangler::mangleType(QualType nonCanon) { - // Only operate on the canonical type! - QualType canon = nonCanon.getCanonicalType(); - - SplitQualType split = canon.split(); +void CXXNameMangler::mangleType(QualType T) { + // If our type is instantiation-dependent but not dependent, we mangle + // it as it was written in the source, removing any top-level sugar. + // Otherwise, use the canonical type. + // + // FIXME: This is an approximation of the instantiation-dependent name + // mangling rules, since we should really be using the type as written and + // augmented via semantic analysis (i.e., with implicit conversions and + // default template arguments) for any instantiation-dependent type. + // Unfortunately, that requires several changes to our AST: + // - Instantiation-dependent TemplateSpecializationTypes will need to be + // uniqued, so that we can handle substitutions properly + // - Default template arguments will need to be represented in the + // TemplateSpecializationType, since they need to be mangled even though + // they aren't written. + // - Conversions on non-type template arguments need to be expressed, since + // they can affect the mangling of sizeof/alignof. + if (!T->isInstantiationDependentType() || T->isDependentType()) + T = T.getCanonicalType(); + else { + // Desugar any types that are purely sugar. + do { + // Don't desugar through template specialization types that aren't + // type aliases. We need to mangle the template arguments as written. + if (const TemplateSpecializationType *TST + = dyn_cast<TemplateSpecializationType>(T)) + if (!TST->isTypeAlias()) + break; + + QualType Desugared + = T.getSingleStepDesugaredType(Context.getASTContext()); + if (Desugared == T) + break; + + T = Desugared; + } while (true); + } + SplitQualType split = T.split(); Qualifiers quals = split.second; const Type *ty = split.first; - bool isSubstitutable = quals || !isa<BuiltinType>(ty); - if (isSubstitutable && mangleSubstitution(canon)) + bool isSubstitutable = quals || !isa<BuiltinType>(T); + if (isSubstitutable && mangleSubstitution(T)) return; // If we're mangling a qualified array type, push the qualifiers to // the element type. - if (quals && isa<ArrayType>(ty)) { - ty = Context.getASTContext().getAsArrayType(canon); + if (quals && isa<ArrayType>(T)) { + ty = Context.getASTContext().getAsArrayType(T); quals = Qualifiers(); - // Note that we don't update canon: we want to add the - // substitution at the canonical type. + // Note that we don't update T: we want to add the + // substitution at the original type. } if (quals) { @@ -1533,7 +1673,7 @@ void CXXNameMangler::mangleType(QualType nonCanon) { // Add the substitution. if (isSubstitutable) - addSubstitution(canon); + addSubstitution(T); } void CXXNameMangler::mangleNameOrStandardSubstitution(const NamedDecl *ND) { @@ -1647,7 +1787,7 @@ void CXXNameMangler::mangleBareFunctionType(const FunctionType *T, for (FunctionProtoType::arg_type_iterator Arg = Proto->arg_type_begin(), ArgEnd = Proto->arg_type_end(); Arg != ArgEnd; ++Arg) - mangleType(*Arg); + mangleType(Context.getASTContext().getSignatureParameterType(*Arg)); FunctionTypeDepth.pop(saved); @@ -1737,7 +1877,11 @@ void CXXNameMangler::mangleType(const TemplateTypeParmType *T) { // <type> ::= <template-param> void CXXNameMangler::mangleType(const SubstTemplateTypeParmPackType *T) { - mangleTemplateParameter(T->getReplacedParameter()->getIndex()); + // FIXME: not clear how to mangle this! + // template <class T...> class A { + // template <class U...> void foo(T(*)(U) x...); + // }; + Out << "_SUBSTPACK_"; } // <type> ::= P <type> # pointer-to @@ -2052,6 +2196,9 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { // <expr-primary> ::= L <type> <value number> E # integer literal // ::= L <type <value float> E # floating literal // ::= L <mangled-name> E # external name + QualType ImplicitlyConvertedToType; + +recurse: switch (E->getStmtClass()) { case Expr::NoStmtClass: #define ABSTRACT_STMT(Type) @@ -2089,6 +2236,7 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case Expr::ObjCProtocolExprClass: case Expr::ObjCSelectorExprClass: case Expr::ObjCStringLiteralClass: + case Expr::ObjCIndirectCopyRestoreExprClass: case Expr::OffsetOfExprClass: case Expr::PredefinedExprClass: case Expr::ShuffleVectorExprClass: @@ -2131,6 +2279,11 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { mangleExpression(cast<CXXDefaultArgExpr>(E)->getExpr(), Arity); break; + case Expr::SubstNonTypeTemplateParmExprClass: + mangleExpression(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(), + Arity); + break; + case Expr::CXXMemberCallExprClass: // fallthrough case Expr::CallExprClass: { const CallExpr *CE = cast<CallExpr>(E); @@ -2209,6 +2362,10 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case Expr::UnresolvedLookupExprClass: { const UnresolvedLookupExpr *ULE = cast<UnresolvedLookupExpr>(E); mangleUnresolvedName(ULE->getQualifier(), 0, ULE->getName(), Arity); + + // All the <unresolved-name> productions end in a + // base-unresolved-name, where <template-args> are just tacked + // onto the end. if (ULE->hasExplicitTemplateArgs()) mangleTemplateArgs(ULE->getExplicitTemplateArgs()); break; @@ -2241,6 +2398,23 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { case Expr::UnaryExprOrTypeTraitExprClass: { const UnaryExprOrTypeTraitExpr *SAE = cast<UnaryExprOrTypeTraitExpr>(E); + + if (!SAE->isInstantiationDependent()) { + // Itanium C++ ABI: + // If the operand of a sizeof or alignof operator is not + // instantiation-dependent it is encoded as an integer literal + // reflecting the result of the operator. + // + // If the result of the operator is implicitly converted to a known + // integer type, that type is used for the literal; otherwise, the type + // of std::size_t or std::ptrdiff_t is used. + QualType T = (ImplicitlyConvertedToType.isNull() || + !ImplicitlyConvertedToType->isIntegerType())? SAE->getType() + : ImplicitlyConvertedToType; + mangleIntegerLiteral(T, SAE->EvaluateAsInt(Context.getASTContext())); + break; + } + switch(SAE->getKind()) { case UETT_SizeOf: Out << 's'; @@ -2344,10 +2518,19 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { } case Expr::ImplicitCastExprClass: { - mangleExpression(cast<ImplicitCastExpr>(E)->getSubExpr(), Arity); - break; + ImplicitlyConvertedToType = E->getType(); + E = cast<ImplicitCastExpr>(E)->getSubExpr(); + goto recurse; } - + + case Expr::ObjCBridgedCastExprClass: { + // Mangle ownership casts as a vendor extended operator __bridge, + // __bridge_transfer, or __bridge_retain. + llvm::StringRef Kind = cast<ObjCBridgedCastExpr>(E)->getBridgeKindName(); + Out << "v1U" << Kind.size() << Kind; + } + // Fall through to mangle the cast itself. + case Expr::CStyleCastExprClass: case Expr::CXXStaticCastExprClass: case Expr::CXXDynamicCastExprClass: @@ -2408,35 +2591,22 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { } case Expr::SubstNonTypeTemplateParmPackExprClass: - mangleTemplateParameter( - cast<SubstNonTypeTemplateParmPackExpr>(E)->getParameterPack()->getIndex()); + // FIXME: not clear how to mangle this! + // template <unsigned N...> class A { + // template <class U...> void foo(U (&x)[N]...); + // }; + Out << "_SUBSTPACK_"; break; case Expr::DependentScopeDeclRefExprClass: { const DependentScopeDeclRefExpr *DRE = cast<DependentScopeDeclRefExpr>(E); - NestedNameSpecifier *NNS = DRE->getQualifier(); - const Type *QTy = NNS->getAsType(); - - // When we're dealing with a nested-name-specifier that has just a - // dependent identifier in it, mangle that as a typename. FIXME: - // It isn't clear that we ever actually want to have such a - // nested-name-specifier; why not just represent it as a typename type? - if (!QTy && NNS->getAsIdentifier() && NNS->getPrefix()) { - QTy = getASTContext().getDependentNameType(ETK_Typename, - NNS->getPrefix(), - NNS->getAsIdentifier()) - .getTypePtr(); - } - assert(QTy && "Qualifier was not type!"); + mangleUnresolvedName(DRE->getQualifier(), 0, DRE->getDeclName(), Arity); - // ::= sr <type> <unqualified-name> # dependent name - // ::= sr <type> <unqualified-name> <template-args> # dependent template-id - Out << "sr"; - mangleType(QualType(QTy, 0)); - mangleUnqualifiedName(0, DRE->getDeclName(), Arity); + // All the <unresolved-name> productions end in a + // base-unresolved-name, where <template-args> are just tacked + // onto the end. if (DRE->hasExplicitTemplateArgs()) mangleTemplateArgs(DRE->getExplicitTemplateArgs()); - break; } @@ -2537,15 +2707,13 @@ void CXXNameMangler::mangleExpression(const Expr *E, unsigned Arity) { else if (const TemplateTemplateParmDecl *TempTP = dyn_cast<TemplateTemplateParmDecl>(Pack)) mangleTemplateParameter(TempTP->getIndex()); - else { - // Note: proposed by Mike Herrick on 11/30/10 - // <expression> ::= sZ <function-param> # size of function parameter pack - Diagnostic &Diags = Context.getDiags(); - unsigned DiagID = Diags.getCustomDiagID(Diagnostic::Error, - "cannot mangle sizeof...(function parameter pack)"); - Diags.Report(DiagID); - return; - } + else + mangleFunctionParam(cast<ParmVarDecl>(Pack)); + break; + } + + case Expr::MaterializeTemporaryExprClass: { + mangleExpression(cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr()); break; } } @@ -2696,12 +2864,15 @@ void CXXNameMangler::mangleTemplateArgs(const TemplateParameterList &PL, } void CXXNameMangler::mangleTemplateArg(const NamedDecl *P, - const TemplateArgument &A) { + TemplateArgument A) { // <template-arg> ::= <type> # type or template // ::= X <expression> E # expression // ::= <expr-primary> # simple expressions // ::= J <template-arg>* E # argument pack - // ::= sp <expression> # pack expansion of (C++0x) + // ::= sp <expression> # pack expansion of (C++0x) + if (!A.isInstantiationDependent() || A.isDependent()) + A = Context.getASTContext().getCanonicalTemplateArgument(A); + switch (A.getKind()) { case TemplateArgument::Null: llvm_unreachable("Cannot mangle NULL template argument"); @@ -2780,6 +2951,18 @@ void CXXNameMangler::mangleTemplateParameter(unsigned Index) { Out << 'T' << (Index - 1) << '_'; } +void CXXNameMangler::mangleExistingSubstitution(QualType type) { + bool result = mangleSubstitution(type); + assert(result && "no existing substitution for type"); + (void) result; +} + +void CXXNameMangler::mangleExistingSubstitution(TemplateName tname) { + bool result = mangleSubstitution(tname); + assert(result && "no existing substitution for template name"); + (void) result; +} + // <substitution> ::= S <seq-id> _ // ::= S_ bool CXXNameMangler::mangleSubstitution(const NamedDecl *ND) { diff --git a/lib/AST/NestedNameSpecifier.cpp b/lib/AST/NestedNameSpecifier.cpp index 2878dff3edc4..f6d4f2513ce4 100644 --- a/lib/AST/NestedNameSpecifier.cpp +++ b/lib/AST/NestedNameSpecifier.cpp @@ -174,6 +174,28 @@ bool NestedNameSpecifier::isDependent() const { return false; } +/// \brief Whether this nested name specifier refers to a dependent +/// type or not. +bool NestedNameSpecifier::isInstantiationDependent() const { + switch (getKind()) { + case Identifier: + // Identifier specifiers always represent dependent types + return true; + + case Namespace: + case NamespaceAlias: + case Global: + return false; + + case TypeSpec: + case TypeSpecWithTemplate: + return getAsType()->isInstantiationDependentType(); + } + + // Necessary to suppress a GCC warning. + return false; +} + bool NestedNameSpecifier::containsUnexpandedParameterPack() const { switch (getKind()) { case Identifier: diff --git a/lib/AST/ParentMap.cpp b/lib/AST/ParentMap.cpp index eca351aec8dd..b7b2005e9f84 100644 --- a/lib/AST/ParentMap.cpp +++ b/lib/AST/ParentMap.cpp @@ -66,6 +66,15 @@ Stmt *ParentMap::getParentIgnoreParenCasts(Stmt *S) const { return S; } +Stmt *ParentMap::getOuterParenParent(Stmt *S) const { + Stmt *Paren = 0; + while (isa<ParenExpr>(S)) { + Paren = S; + S = getParent(S); + }; + return Paren; +} + bool ParentMap::isConsumedExpr(Expr* E) const { Stmt *P = getParent(E); Stmt *DirectChild = E; diff --git a/lib/AST/RecordLayoutBuilder.cpp b/lib/AST/RecordLayoutBuilder.cpp index de0b1d0ed9db..5636a6f0f64a 100644 --- a/lib/AST/RecordLayoutBuilder.cpp +++ b/lib/AST/RecordLayoutBuilder.cpp @@ -1242,12 +1242,11 @@ void RecordLayoutBuilder::Layout(const ObjCInterfaceDecl *D) { } InitializeLayout(D); - + ObjCInterfaceDecl *OI = const_cast<ObjCInterfaceDecl*>(D); // Layout each ivar sequentially. - llvm::SmallVector<ObjCIvarDecl*, 16> Ivars; - Context.ShallowCollectObjCIvars(D, Ivars); - for (unsigned i = 0, e = Ivars.size(); i != e; ++i) - LayoutField(Ivars[i]); + for (ObjCIvarDecl *IVD = OI->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + LayoutField(IVD); // Finally, round the size of the total struct up to the alignment of the // struct itself. diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp index 380ad94ca224..fd6f21d43b59 100644 --- a/lib/AST/Stmt.cpp +++ b/lib/AST/Stmt.cpp @@ -20,7 +20,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" #include "clang/Basic/TargetInfo.h" -#include <cstdio> +#include "llvm/Support/raw_ostream.h" using namespace clang; static struct StmtClassNameTable { @@ -54,23 +54,24 @@ void Stmt::PrintStats() { getStmtInfoTableEntry(Stmt::NullStmtClass); unsigned sum = 0; - fprintf(stderr, "*** Stmt/Expr Stats:\n"); + llvm::errs() << "\n*** Stmt/Expr Stats:\n"; for (int i = 0; i != Stmt::lastStmtConstant+1; i++) { if (StmtClassInfo[i].Name == 0) continue; sum += StmtClassInfo[i].Counter; } - fprintf(stderr, " %d stmts/exprs total.\n", sum); + llvm::errs() << " " << sum << " stmts/exprs total.\n"; sum = 0; for (int i = 0; i != Stmt::lastStmtConstant+1; i++) { if (StmtClassInfo[i].Name == 0) continue; if (StmtClassInfo[i].Counter == 0) continue; - fprintf(stderr, " %d %s, %d each (%d bytes)\n", - StmtClassInfo[i].Counter, StmtClassInfo[i].Name, - StmtClassInfo[i].Size, - StmtClassInfo[i].Counter*StmtClassInfo[i].Size); + llvm::errs() << " " << StmtClassInfo[i].Counter << " " + << StmtClassInfo[i].Name << ", " << StmtClassInfo[i].Size + << " each (" << StmtClassInfo[i].Counter*StmtClassInfo[i].Size + << " bytes)\n"; sum += StmtClassInfo[i].Counter*StmtClassInfo[i].Size; } - fprintf(stderr, "Total bytes = %d\n", sum); + + llvm::errs() << "Total bytes = " << sum << "\n"; } void Stmt::addStmtClass(StmtClass s) { @@ -84,6 +85,18 @@ bool Stmt::CollectingStats(bool Enable) { return StatSwitch; } +Stmt *Stmt::IgnoreImplicit() { + Stmt *s = this; + + if (ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(s)) + s = ewc->getSubExpr(); + + while (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(s)) + s = ice->getSubExpr(); + + return s; +} + namespace { struct good {}; struct bad {}; @@ -365,6 +378,10 @@ unsigned AsmStmt::AnalyzeAsmString(llvm::SmallVectorImpl<AsmStringPiece>&Pieces, // Handle %x4 and %x[foo] by capturing x as the modifier character. char Modifier = '\0'; if (isalpha(EscapedChar)) { + if (CurPtr == StrEnd) { // Premature end. + DiagOffs = CurPtr-StrStart-1; + return diag::err_asm_invalid_escape; + } Modifier = EscapedChar; EscapedChar = *CurPtr++; } diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp index 87588e451884..f705a84c7cf4 100644 --- a/lib/AST/StmtPrinter.cpp +++ b/lib/AST/StmtPrinter.cpp @@ -449,6 +449,12 @@ void StmtPrinter::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *Node) { OS << "\n"; } +void StmtPrinter::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *Node) { + Indent() << "@autoreleasepool"; + PrintRawCompoundStmt(dyn_cast<CompoundStmt>(Node->getSubStmt())); + OS << "\n"; +} + void StmtPrinter::PrintRawCXXCatchStmt(CXXCatchStmt *Node) { OS << "catch ("; if (Decl *ExDecl = Node->getExceptionDecl()) @@ -1407,6 +1413,15 @@ void StmtPrinter::VisitSubstNonTypeTemplateParmPackExpr( OS << Node->getParameterPack()->getNameAsString(); } +void StmtPrinter::VisitSubstNonTypeTemplateParmExpr( + SubstNonTypeTemplateParmExpr *Node) { + Visit(Node->getReplacement()); +} + +void StmtPrinter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *Node){ + PrintExpr(Node->GetTemporaryExpr()); +} + // Obj-C void StmtPrinter::VisitObjCStringLiteral(ObjCStringLiteral *Node) { @@ -1464,6 +1479,17 @@ void StmtPrinter::VisitObjCMessageExpr(ObjCMessageExpr *Mess) { OS << "]"; } +void +StmtPrinter::VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + PrintExpr(E->getSubExpr()); +} + +void +StmtPrinter::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + OS << "(" << E->getBridgeKindName() << E->getType().getAsString(Policy) + << ")"; + PrintExpr(E->getSubExpr()); +} void StmtPrinter::VisitBlockExpr(BlockExpr *Node) { BlockDecl *BD = Node->getBlockDecl(); diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp index b117cd9a525f..120c9e50a92a 100644 --- a/lib/AST/StmtProfile.cpp +++ b/lib/AST/StmtProfile.cpp @@ -23,7 +23,7 @@ using namespace clang; namespace { - class StmtProfiler : public StmtVisitor<StmtProfiler> { + class StmtProfiler : public ConstStmtVisitor<StmtProfiler> { llvm::FoldingSetNodeID &ID; const ASTContext &Context; bool Canonical; @@ -33,14 +33,14 @@ namespace { bool Canonical) : ID(ID), Context(Context), Canonical(Canonical) { } - void VisitStmt(Stmt *S); + void VisitStmt(const Stmt *S); -#define STMT(Node, Base) void Visit##Node(Node *S); +#define STMT(Node, Base) void Visit##Node(const Node *S); #include "clang/AST/StmtNodes.inc" /// \brief Visit a declaration that is referenced within an expression /// or statement. - void VisitDecl(Decl *D); + void VisitDecl(const Decl *D); /// \brief Visit a type that is referenced within an expression or /// statement. @@ -59,96 +59,97 @@ namespace { /// \brief Visit template arguments that occur within an expression or /// statement. - void VisitTemplateArguments(const TemplateArgumentLoc *Args, unsigned NumArgs); + void VisitTemplateArguments(const TemplateArgumentLoc *Args, + unsigned NumArgs); /// \brief Visit a single template argument. void VisitTemplateArgument(const TemplateArgument &Arg); }; } -void StmtProfiler::VisitStmt(Stmt *S) { +void StmtProfiler::VisitStmt(const Stmt *S) { ID.AddInteger(S->getStmtClass()); - for (Stmt::child_range C = S->children(); C; ++C) + for (Stmt::const_child_range C = S->children(); C; ++C) Visit(*C); } -void StmtProfiler::VisitDeclStmt(DeclStmt *S) { +void StmtProfiler::VisitDeclStmt(const DeclStmt *S) { VisitStmt(S); - for (DeclStmt::decl_iterator D = S->decl_begin(), DEnd = S->decl_end(); + for (DeclStmt::const_decl_iterator D = S->decl_begin(), DEnd = S->decl_end(); D != DEnd; ++D) VisitDecl(*D); } -void StmtProfiler::VisitNullStmt(NullStmt *S) { +void StmtProfiler::VisitNullStmt(const NullStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitCompoundStmt(CompoundStmt *S) { +void StmtProfiler::VisitCompoundStmt(const CompoundStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitSwitchCase(SwitchCase *S) { +void StmtProfiler::VisitSwitchCase(const SwitchCase *S) { VisitStmt(S); } -void StmtProfiler::VisitCaseStmt(CaseStmt *S) { +void StmtProfiler::VisitCaseStmt(const CaseStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitDefaultStmt(DefaultStmt *S) { +void StmtProfiler::VisitDefaultStmt(const DefaultStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitLabelStmt(LabelStmt *S) { +void StmtProfiler::VisitLabelStmt(const LabelStmt *S) { VisitStmt(S); VisitDecl(S->getDecl()); } -void StmtProfiler::VisitIfStmt(IfStmt *S) { +void StmtProfiler::VisitIfStmt(const IfStmt *S) { VisitStmt(S); VisitDecl(S->getConditionVariable()); } -void StmtProfiler::VisitSwitchStmt(SwitchStmt *S) { +void StmtProfiler::VisitSwitchStmt(const SwitchStmt *S) { VisitStmt(S); VisitDecl(S->getConditionVariable()); } -void StmtProfiler::VisitWhileStmt(WhileStmt *S) { +void StmtProfiler::VisitWhileStmt(const WhileStmt *S) { VisitStmt(S); VisitDecl(S->getConditionVariable()); } -void StmtProfiler::VisitDoStmt(DoStmt *S) { +void StmtProfiler::VisitDoStmt(const DoStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitForStmt(ForStmt *S) { +void StmtProfiler::VisitForStmt(const ForStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitGotoStmt(GotoStmt *S) { +void StmtProfiler::VisitGotoStmt(const GotoStmt *S) { VisitStmt(S); VisitDecl(S->getLabel()); } -void StmtProfiler::VisitIndirectGotoStmt(IndirectGotoStmt *S) { +void StmtProfiler::VisitIndirectGotoStmt(const IndirectGotoStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitContinueStmt(ContinueStmt *S) { +void StmtProfiler::VisitContinueStmt(const ContinueStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitBreakStmt(BreakStmt *S) { +void StmtProfiler::VisitBreakStmt(const BreakStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitReturnStmt(ReturnStmt *S) { +void StmtProfiler::VisitReturnStmt(const ReturnStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitAsmStmt(AsmStmt *S) { +void StmtProfiler::VisitAsmStmt(const AsmStmt *S) { VisitStmt(S); ID.AddBoolean(S->isVolatile()); ID.AddBoolean(S->isSimple()); @@ -168,63 +169,69 @@ void StmtProfiler::VisitAsmStmt(AsmStmt *S) { VisitStringLiteral(S->getClobber(I)); } -void StmtProfiler::VisitCXXCatchStmt(CXXCatchStmt *S) { +void StmtProfiler::VisitCXXCatchStmt(const CXXCatchStmt *S) { VisitStmt(S); VisitType(S->getCaughtType()); } -void StmtProfiler::VisitCXXTryStmt(CXXTryStmt *S) { +void StmtProfiler::VisitCXXTryStmt(const CXXTryStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitCXXForRangeStmt(CXXForRangeStmt *S) { +void StmtProfiler::VisitCXXForRangeStmt(const CXXForRangeStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitSEHTryStmt(SEHTryStmt *S) { +void StmtProfiler::VisitSEHTryStmt(const SEHTryStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitSEHFinallyStmt(SEHFinallyStmt *S) { +void StmtProfiler::VisitSEHFinallyStmt(const SEHFinallyStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitSEHExceptStmt(SEHExceptStmt *S) { +void StmtProfiler::VisitSEHExceptStmt(const SEHExceptStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitObjCForCollectionStmt(ObjCForCollectionStmt *S) { +void StmtProfiler::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitObjCAtCatchStmt(ObjCAtCatchStmt *S) { +void StmtProfiler::VisitObjCAtCatchStmt(const ObjCAtCatchStmt *S) { VisitStmt(S); ID.AddBoolean(S->hasEllipsis()); if (S->getCatchParamDecl()) VisitType(S->getCatchParamDecl()->getType()); } -void StmtProfiler::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { +void StmtProfiler::VisitObjCAtFinallyStmt(const ObjCAtFinallyStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { +void StmtProfiler::VisitObjCAtTryStmt(const ObjCAtTryStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *S) { +void +StmtProfiler::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitObjCAtThrowStmt(ObjCAtThrowStmt *S) { +void StmtProfiler::VisitObjCAtThrowStmt(const ObjCAtThrowStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitExpr(Expr *S) { +void +StmtProfiler::VisitObjCAutoreleasePoolStmt(const ObjCAutoreleasePoolStmt *S) { VisitStmt(S); } -void StmtProfiler::VisitDeclRefExpr(DeclRefExpr *S) { +void StmtProfiler::VisitExpr(const Expr *S) { + VisitStmt(S); +} + +void StmtProfiler::VisitDeclRefExpr(const DeclRefExpr *S) { VisitExpr(S); if (!Canonical) VisitNestedNameSpecifier(S->getQualifier()); @@ -233,52 +240,52 @@ void StmtProfiler::VisitDeclRefExpr(DeclRefExpr *S) { VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs()); } -void StmtProfiler::VisitPredefinedExpr(PredefinedExpr *S) { +void StmtProfiler::VisitPredefinedExpr(const PredefinedExpr *S) { VisitExpr(S); ID.AddInteger(S->getIdentType()); } -void StmtProfiler::VisitIntegerLiteral(IntegerLiteral *S) { +void StmtProfiler::VisitIntegerLiteral(const IntegerLiteral *S) { VisitExpr(S); S->getValue().Profile(ID); } -void StmtProfiler::VisitCharacterLiteral(CharacterLiteral *S) { +void StmtProfiler::VisitCharacterLiteral(const CharacterLiteral *S) { VisitExpr(S); ID.AddBoolean(S->isWide()); ID.AddInteger(S->getValue()); } -void StmtProfiler::VisitFloatingLiteral(FloatingLiteral *S) { +void StmtProfiler::VisitFloatingLiteral(const FloatingLiteral *S) { VisitExpr(S); S->getValue().Profile(ID); ID.AddBoolean(S->isExact()); } -void StmtProfiler::VisitImaginaryLiteral(ImaginaryLiteral *S) { +void StmtProfiler::VisitImaginaryLiteral(const ImaginaryLiteral *S) { VisitExpr(S); } -void StmtProfiler::VisitStringLiteral(StringLiteral *S) { +void StmtProfiler::VisitStringLiteral(const StringLiteral *S) { VisitExpr(S); ID.AddString(S->getString()); ID.AddBoolean(S->isWide()); } -void StmtProfiler::VisitParenExpr(ParenExpr *S) { +void StmtProfiler::VisitParenExpr(const ParenExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitParenListExpr(ParenListExpr *S) { +void StmtProfiler::VisitParenListExpr(const ParenListExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitUnaryOperator(UnaryOperator *S) { +void StmtProfiler::VisitUnaryOperator(const UnaryOperator *S) { VisitExpr(S); ID.AddInteger(S->getOpcode()); } -void StmtProfiler::VisitOffsetOfExpr(OffsetOfExpr *S) { +void StmtProfiler::VisitOffsetOfExpr(const OffsetOfExpr *S) { VisitType(S->getTypeSourceInfo()->getType()); unsigned n = S->getNumComponents(); for (unsigned i = 0; i < n; ++i) { @@ -306,22 +313,23 @@ void StmtProfiler::VisitOffsetOfExpr(OffsetOfExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *S) { +void +StmtProfiler::VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *S) { VisitExpr(S); ID.AddInteger(S->getKind()); if (S->isArgumentType()) VisitType(S->getArgumentType()); } -void StmtProfiler::VisitArraySubscriptExpr(ArraySubscriptExpr *S) { +void StmtProfiler::VisitArraySubscriptExpr(const ArraySubscriptExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitCallExpr(CallExpr *S) { +void StmtProfiler::VisitCallExpr(const CallExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitMemberExpr(MemberExpr *S) { +void StmtProfiler::VisitMemberExpr(const MemberExpr *S) { VisitExpr(S); VisitDecl(S->getMemberDecl()); if (!Canonical) @@ -329,72 +337,74 @@ void StmtProfiler::VisitMemberExpr(MemberExpr *S) { ID.AddBoolean(S->isArrow()); } -void StmtProfiler::VisitCompoundLiteralExpr(CompoundLiteralExpr *S) { +void StmtProfiler::VisitCompoundLiteralExpr(const CompoundLiteralExpr *S) { VisitExpr(S); ID.AddBoolean(S->isFileScope()); } -void StmtProfiler::VisitCastExpr(CastExpr *S) { +void StmtProfiler::VisitCastExpr(const CastExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitImplicitCastExpr(ImplicitCastExpr *S) { +void StmtProfiler::VisitImplicitCastExpr(const ImplicitCastExpr *S) { VisitCastExpr(S); ID.AddInteger(S->getValueKind()); } -void StmtProfiler::VisitExplicitCastExpr(ExplicitCastExpr *S) { +void StmtProfiler::VisitExplicitCastExpr(const ExplicitCastExpr *S) { VisitCastExpr(S); VisitType(S->getTypeAsWritten()); } -void StmtProfiler::VisitCStyleCastExpr(CStyleCastExpr *S) { +void StmtProfiler::VisitCStyleCastExpr(const CStyleCastExpr *S) { VisitExplicitCastExpr(S); } -void StmtProfiler::VisitBinaryOperator(BinaryOperator *S) { +void StmtProfiler::VisitBinaryOperator(const BinaryOperator *S) { VisitExpr(S); ID.AddInteger(S->getOpcode()); } -void StmtProfiler::VisitCompoundAssignOperator(CompoundAssignOperator *S) { +void +StmtProfiler::VisitCompoundAssignOperator(const CompoundAssignOperator *S) { VisitBinaryOperator(S); } -void StmtProfiler::VisitConditionalOperator(ConditionalOperator *S) { +void StmtProfiler::VisitConditionalOperator(const ConditionalOperator *S) { VisitExpr(S); } -void StmtProfiler::VisitBinaryConditionalOperator(BinaryConditionalOperator *S){ +void StmtProfiler::VisitBinaryConditionalOperator( + const BinaryConditionalOperator *S) { VisitExpr(S); } -void StmtProfiler::VisitAddrLabelExpr(AddrLabelExpr *S) { +void StmtProfiler::VisitAddrLabelExpr(const AddrLabelExpr *S) { VisitExpr(S); VisitDecl(S->getLabel()); } -void StmtProfiler::VisitStmtExpr(StmtExpr *S) { +void StmtProfiler::VisitStmtExpr(const StmtExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitShuffleVectorExpr(ShuffleVectorExpr *S) { +void StmtProfiler::VisitShuffleVectorExpr(const ShuffleVectorExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitChooseExpr(ChooseExpr *S) { +void StmtProfiler::VisitChooseExpr(const ChooseExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitGNUNullExpr(GNUNullExpr *S) { +void StmtProfiler::VisitGNUNullExpr(const GNUNullExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitVAArgExpr(VAArgExpr *S) { +void StmtProfiler::VisitVAArgExpr(const VAArgExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitInitListExpr(InitListExpr *S) { +void StmtProfiler::VisitInitListExpr(const InitListExpr *S) { if (S->getSyntacticForm()) { VisitInitListExpr(S->getSyntacticForm()); return; @@ -403,11 +413,11 @@ void StmtProfiler::VisitInitListExpr(InitListExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitDesignatedInitExpr(DesignatedInitExpr *S) { +void StmtProfiler::VisitDesignatedInitExpr(const DesignatedInitExpr *S) { VisitExpr(S); ID.AddBoolean(S->usesGNUSyntax()); - for (DesignatedInitExpr::designators_iterator D = S->designators_begin(), - DEnd = S->designators_end(); + for (DesignatedInitExpr::const_designators_iterator D = + S->designators_begin(), DEnd = S->designators_end(); D != DEnd; ++D) { if (D->isFieldDesignator()) { ID.AddInteger(0); @@ -425,28 +435,28 @@ void StmtProfiler::VisitDesignatedInitExpr(DesignatedInitExpr *S) { } } -void StmtProfiler::VisitImplicitValueInitExpr(ImplicitValueInitExpr *S) { +void StmtProfiler::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitExtVectorElementExpr(ExtVectorElementExpr *S) { +void StmtProfiler::VisitExtVectorElementExpr(const ExtVectorElementExpr *S) { VisitExpr(S); VisitName(&S->getAccessor()); } -void StmtProfiler::VisitBlockExpr(BlockExpr *S) { +void StmtProfiler::VisitBlockExpr(const BlockExpr *S) { VisitExpr(S); VisitDecl(S->getBlockDecl()); } -void StmtProfiler::VisitBlockDeclRefExpr(BlockDeclRefExpr *S) { +void StmtProfiler::VisitBlockDeclRefExpr(const BlockDeclRefExpr *S) { VisitExpr(S); VisitDecl(S->getDecl()); ID.AddBoolean(S->isByRef()); ID.AddBoolean(S->isConstQualAdded()); } -void StmtProfiler::VisitGenericSelectionExpr(GenericSelectionExpr *S) { +void StmtProfiler::VisitGenericSelectionExpr(const GenericSelectionExpr *S) { VisitExpr(S); for (unsigned i = 0; i != S->getNumAssocs(); ++i) { QualType T = S->getAssocType(i); @@ -458,7 +468,7 @@ void StmtProfiler::VisitGenericSelectionExpr(GenericSelectionExpr *S) { } } -static Stmt::StmtClass DecodeOperatorCall(CXXOperatorCallExpr *S, +static Stmt::StmtClass DecodeOperatorCall(const CXXOperatorCallExpr *S, UnaryOperatorKind &UnaryOp, BinaryOperatorKind &BinaryOp) { switch (S->getOperator()) { @@ -645,7 +655,7 @@ static Stmt::StmtClass DecodeOperatorCall(CXXOperatorCallExpr *S, } -void StmtProfiler::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *S) { +void StmtProfiler::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *S) { if (S->isTypeDependent()) { // Type-dependent operator calls are profiled like their underlying // syntactic operator. @@ -671,97 +681,100 @@ void StmtProfiler::VisitCXXOperatorCallExpr(CXXOperatorCallExpr *S) { ID.AddInteger(S->getOperator()); } -void StmtProfiler::VisitCXXMemberCallExpr(CXXMemberCallExpr *S) { +void StmtProfiler::VisitCXXMemberCallExpr(const CXXMemberCallExpr *S) { VisitCallExpr(S); } -void StmtProfiler::VisitCUDAKernelCallExpr(CUDAKernelCallExpr *S) { +void StmtProfiler::VisitCUDAKernelCallExpr(const CUDAKernelCallExpr *S) { VisitCallExpr(S); } -void StmtProfiler::VisitAsTypeExpr(AsTypeExpr *S) { +void StmtProfiler::VisitAsTypeExpr(const AsTypeExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitCXXNamedCastExpr(CXXNamedCastExpr *S) { +void StmtProfiler::VisitCXXNamedCastExpr(const CXXNamedCastExpr *S) { VisitExplicitCastExpr(S); } -void StmtProfiler::VisitCXXStaticCastExpr(CXXStaticCastExpr *S) { +void StmtProfiler::VisitCXXStaticCastExpr(const CXXStaticCastExpr *S) { VisitCXXNamedCastExpr(S); } -void StmtProfiler::VisitCXXDynamicCastExpr(CXXDynamicCastExpr *S) { +void StmtProfiler::VisitCXXDynamicCastExpr(const CXXDynamicCastExpr *S) { VisitCXXNamedCastExpr(S); } -void StmtProfiler::VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr *S) { +void +StmtProfiler::VisitCXXReinterpretCastExpr(const CXXReinterpretCastExpr *S) { VisitCXXNamedCastExpr(S); } -void StmtProfiler::VisitCXXConstCastExpr(CXXConstCastExpr *S) { +void StmtProfiler::VisitCXXConstCastExpr(const CXXConstCastExpr *S) { VisitCXXNamedCastExpr(S); } -void StmtProfiler::VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *S) { +void StmtProfiler::VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *S) { VisitExpr(S); ID.AddBoolean(S->getValue()); } -void StmtProfiler::VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr *S) { +void StmtProfiler::VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitCXXTypeidExpr(CXXTypeidExpr *S) { +void StmtProfiler::VisitCXXTypeidExpr(const CXXTypeidExpr *S) { VisitExpr(S); if (S->isTypeOperand()) VisitType(S->getTypeOperand()); } -void StmtProfiler::VisitCXXUuidofExpr(CXXUuidofExpr *S) { +void StmtProfiler::VisitCXXUuidofExpr(const CXXUuidofExpr *S) { VisitExpr(S); if (S->isTypeOperand()) VisitType(S->getTypeOperand()); } -void StmtProfiler::VisitCXXThisExpr(CXXThisExpr *S) { +void StmtProfiler::VisitCXXThisExpr(const CXXThisExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitCXXThrowExpr(CXXThrowExpr *S) { +void StmtProfiler::VisitCXXThrowExpr(const CXXThrowExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *S) { +void StmtProfiler::VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *S) { VisitExpr(S); VisitDecl(S->getParam()); } -void StmtProfiler::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *S) { +void StmtProfiler::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *S) { VisitExpr(S); VisitDecl( const_cast<CXXDestructorDecl *>(S->getTemporary()->getDestructor())); } -void StmtProfiler::VisitCXXConstructExpr(CXXConstructExpr *S) { +void StmtProfiler::VisitCXXConstructExpr(const CXXConstructExpr *S) { VisitExpr(S); VisitDecl(S->getConstructor()); ID.AddBoolean(S->isElidable()); } -void StmtProfiler::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *S) { +void StmtProfiler::VisitCXXFunctionalCastExpr(const CXXFunctionalCastExpr *S) { VisitExplicitCastExpr(S); } -void StmtProfiler::VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *S) { +void +StmtProfiler::VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *S) { VisitCXXConstructExpr(S); } -void StmtProfiler::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *S) { +void +StmtProfiler::VisitCXXScalarValueInitExpr(const CXXScalarValueInitExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitCXXDeleteExpr(CXXDeleteExpr *S) { +void StmtProfiler::VisitCXXDeleteExpr(const CXXDeleteExpr *S) { VisitExpr(S); ID.AddBoolean(S->isGlobalDelete()); ID.AddBoolean(S->isArrayForm()); @@ -769,7 +782,7 @@ void StmtProfiler::VisitCXXDeleteExpr(CXXDeleteExpr *S) { } -void StmtProfiler::VisitCXXNewExpr(CXXNewExpr *S) { +void StmtProfiler::VisitCXXNewExpr(const CXXNewExpr *S) { VisitExpr(S); VisitType(S->getAllocatedType()); VisitDecl(S->getOperatorNew()); @@ -783,14 +796,15 @@ void StmtProfiler::VisitCXXNewExpr(CXXNewExpr *S) { ID.AddInteger(S->getNumConstructorArgs()); } -void StmtProfiler::VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *S) { +void +StmtProfiler::VisitCXXPseudoDestructorExpr(const CXXPseudoDestructorExpr *S) { VisitExpr(S); ID.AddBoolean(S->isArrow()); VisitNestedNameSpecifier(S->getQualifier()); VisitType(S->getDestroyedType()); } -void StmtProfiler::VisitOverloadExpr(OverloadExpr *S) { +void StmtProfiler::VisitOverloadExpr(const OverloadExpr *S) { VisitExpr(S); VisitNestedNameSpecifier(S->getQualifier()); VisitName(S->getName()); @@ -801,37 +815,37 @@ void StmtProfiler::VisitOverloadExpr(OverloadExpr *S) { } void -StmtProfiler::VisitUnresolvedLookupExpr(UnresolvedLookupExpr *S) { +StmtProfiler::VisitUnresolvedLookupExpr(const UnresolvedLookupExpr *S) { VisitOverloadExpr(S); } -void StmtProfiler::VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *S) { +void StmtProfiler::VisitUnaryTypeTraitExpr(const UnaryTypeTraitExpr *S) { VisitExpr(S); ID.AddInteger(S->getTrait()); VisitType(S->getQueriedType()); } -void StmtProfiler::VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *S) { +void StmtProfiler::VisitBinaryTypeTraitExpr(const BinaryTypeTraitExpr *S) { VisitExpr(S); ID.AddInteger(S->getTrait()); VisitType(S->getLhsType()); VisitType(S->getRhsType()); } -void StmtProfiler::VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *S) { +void StmtProfiler::VisitArrayTypeTraitExpr(const ArrayTypeTraitExpr *S) { VisitExpr(S); ID.AddInteger(S->getTrait()); VisitType(S->getQueriedType()); } -void StmtProfiler::VisitExpressionTraitExpr(ExpressionTraitExpr *S) { +void StmtProfiler::VisitExpressionTraitExpr(const ExpressionTraitExpr *S) { VisitExpr(S); ID.AddInteger(S->getTrait()); VisitExpr(S->getQueriedExpression()); } -void -StmtProfiler::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) { +void StmtProfiler::VisitDependentScopeDeclRefExpr( + const DependentScopeDeclRefExpr *S) { VisitExpr(S); VisitName(S->getDeclName()); VisitNestedNameSpecifier(S->getQualifier()); @@ -840,18 +854,18 @@ StmtProfiler::VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *S) { VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs()); } -void StmtProfiler::VisitExprWithCleanups(ExprWithCleanups *S) { +void StmtProfiler::VisitExprWithCleanups(const ExprWithCleanups *S) { VisitExpr(S); } -void -StmtProfiler::VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *S) { +void StmtProfiler::VisitCXXUnresolvedConstructExpr( + const CXXUnresolvedConstructExpr *S) { VisitExpr(S); VisitType(S->getTypeAsWritten()); } -void -StmtProfiler::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *S) { +void StmtProfiler::VisitCXXDependentScopeMemberExpr( + const CXXDependentScopeMemberExpr *S) { ID.AddBoolean(S->isImplicitAccess()); if (!S->isImplicitAccess()) { VisitExpr(S); @@ -864,7 +878,7 @@ StmtProfiler::VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *S) { VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs()); } -void StmtProfiler::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *S) { +void StmtProfiler::VisitUnresolvedMemberExpr(const UnresolvedMemberExpr *S) { ID.AddBoolean(S->isImplicitAccess()); if (!S->isImplicitAccess()) { VisitExpr(S); @@ -877,57 +891,68 @@ void StmtProfiler::VisitUnresolvedMemberExpr(UnresolvedMemberExpr *S) { VisitTemplateArguments(S->getTemplateArgs(), S->getNumTemplateArgs()); } -void StmtProfiler::VisitCXXNoexceptExpr(CXXNoexceptExpr *S) { +void StmtProfiler::VisitCXXNoexceptExpr(const CXXNoexceptExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitPackExpansionExpr(PackExpansionExpr *S) { +void StmtProfiler::VisitPackExpansionExpr(const PackExpansionExpr *S) { VisitExpr(S); } -void StmtProfiler::VisitSizeOfPackExpr(SizeOfPackExpr *S) { +void StmtProfiler::VisitSizeOfPackExpr(const SizeOfPackExpr *S) { VisitExpr(S); VisitDecl(S->getPack()); } void StmtProfiler::VisitSubstNonTypeTemplateParmPackExpr( - SubstNonTypeTemplateParmPackExpr *S) { + const SubstNonTypeTemplateParmPackExpr *S) { VisitExpr(S); VisitDecl(S->getParameterPack()); VisitTemplateArgument(S->getArgumentPack()); } -void StmtProfiler::VisitOpaqueValueExpr(OpaqueValueExpr *E) { +void StmtProfiler::VisitSubstNonTypeTemplateParmExpr( + const SubstNonTypeTemplateParmExpr *E) { + // Profile exactly as the replacement expression. + Visit(E->getReplacement()); +} + +void StmtProfiler::VisitMaterializeTemporaryExpr( + const MaterializeTemporaryExpr *S) { + VisitExpr(S); +} + +void StmtProfiler::VisitOpaqueValueExpr(const OpaqueValueExpr *E) { VisitExpr(E); } -void StmtProfiler::VisitObjCStringLiteral(ObjCStringLiteral *S) { +void StmtProfiler::VisitObjCStringLiteral(const ObjCStringLiteral *S) { VisitExpr(S); } -void StmtProfiler::VisitObjCEncodeExpr(ObjCEncodeExpr *S) { +void StmtProfiler::VisitObjCEncodeExpr(const ObjCEncodeExpr *S) { VisitExpr(S); VisitType(S->getEncodedType()); } -void StmtProfiler::VisitObjCSelectorExpr(ObjCSelectorExpr *S) { +void StmtProfiler::VisitObjCSelectorExpr(const ObjCSelectorExpr *S) { VisitExpr(S); VisitName(S->getSelector()); } -void StmtProfiler::VisitObjCProtocolExpr(ObjCProtocolExpr *S) { +void StmtProfiler::VisitObjCProtocolExpr(const ObjCProtocolExpr *S) { VisitExpr(S); VisitDecl(S->getProtocol()); } -void StmtProfiler::VisitObjCIvarRefExpr(ObjCIvarRefExpr *S) { +void StmtProfiler::VisitObjCIvarRefExpr(const ObjCIvarRefExpr *S) { VisitExpr(S); VisitDecl(S->getDecl()); ID.AddBoolean(S->isArrow()); ID.AddBoolean(S->isFreeIvar()); } -void StmtProfiler::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *S) { +void StmtProfiler::VisitObjCPropertyRefExpr(const ObjCPropertyRefExpr *S) { VisitExpr(S); if (S->isImplicitProperty()) { VisitDecl(S->getImplicitPropertyGetter()); @@ -941,22 +966,34 @@ void StmtProfiler::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *S) { } } -void StmtProfiler::VisitObjCMessageExpr(ObjCMessageExpr *S) { +void StmtProfiler::VisitObjCMessageExpr(const ObjCMessageExpr *S) { VisitExpr(S); VisitName(S->getSelector()); VisitDecl(S->getMethodDecl()); } -void StmtProfiler::VisitObjCIsaExpr(ObjCIsaExpr *S) { +void StmtProfiler::VisitObjCIsaExpr(const ObjCIsaExpr *S) { VisitExpr(S); ID.AddBoolean(S->isArrow()); } -void StmtProfiler::VisitDecl(Decl *D) { +void StmtProfiler::VisitObjCIndirectCopyRestoreExpr( + const ObjCIndirectCopyRestoreExpr *S) { + VisitExpr(S); + ID.AddBoolean(S->shouldCopy()); +} + +void StmtProfiler::VisitObjCBridgedCastExpr(const ObjCBridgedCastExpr *S) { + VisitExplicitCastExpr(S); + ID.AddBoolean(S->getBridgeKind()); +} + +void StmtProfiler::VisitDecl(const Decl *D) { ID.AddInteger(D? D->getKind() : 0); if (Canonical && D) { - if (NonTypeTemplateParmDecl *NTTP = dyn_cast<NonTypeTemplateParmDecl>(D)) { + if (const NonTypeTemplateParmDecl *NTTP = + dyn_cast<NonTypeTemplateParmDecl>(D)) { ID.AddInteger(NTTP->getDepth()); ID.AddInteger(NTTP->getIndex()); ID.AddBoolean(NTTP->isParameterPack()); @@ -964,7 +1001,7 @@ void StmtProfiler::VisitDecl(Decl *D) { return; } - if (ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { + if (const ParmVarDecl *Parm = dyn_cast<ParmVarDecl>(D)) { // The Itanium C++ ABI uses the type, scope depth, and scope // index of a parameter when mangling expressions that involve // function parameters, so we will use the parameter's type for @@ -978,7 +1015,8 @@ void StmtProfiler::VisitDecl(Decl *D) { return; } - if (TemplateTemplateParmDecl *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) { + if (const TemplateTemplateParmDecl *TTP = + dyn_cast<TemplateTemplateParmDecl>(D)) { ID.AddInteger(TTP->getDepth()); ID.AddInteger(TTP->getIndex()); ID.AddBoolean(TTP->isParameterPack()); @@ -1058,7 +1096,7 @@ void StmtProfiler::VisitTemplateArgument(const TemplateArgument &Arg) { } void Stmt::Profile(llvm::FoldingSetNodeID &ID, const ASTContext &Context, - bool Canonical) { + bool Canonical) const { StmtProfiler Profiler(ID, Context, Canonical); Profiler.Visit(this); } diff --git a/lib/AST/TemplateBase.cpp b/lib/AST/TemplateBase.cpp index 6114a5a051be..56c6e7bc47c1 100644 --- a/lib/AST/TemplateBase.cpp +++ b/lib/AST/TemplateBase.cpp @@ -104,6 +104,45 @@ bool TemplateArgument::isDependent() const { return false; } +bool TemplateArgument::isInstantiationDependent() const { + switch (getKind()) { + case Null: + assert(false && "Should not have a NULL template argument"); + return false; + + case Type: + return getAsType()->isInstantiationDependentType(); + + case Template: + return getAsTemplate().isInstantiationDependent(); + + case TemplateExpansion: + return true; + + case Declaration: + if (DeclContext *DC = dyn_cast<DeclContext>(getAsDecl())) + return DC->isDependentContext(); + return getAsDecl()->getDeclContext()->isDependentContext(); + + case Integral: + // Never dependent + return false; + + case Expression: + return getAsExpr()->isInstantiationDependent(); + + case Pack: + for (pack_iterator P = pack_begin(), PEnd = pack_end(); P != PEnd; ++P) { + if (P->isInstantiationDependent()) + return true; + } + + return false; + } + + return false; +} + bool TemplateArgument::isPackExpansion() const { switch (getKind()) { case Null: @@ -277,8 +316,10 @@ void TemplateArgument::print(const PrintingPolicy &Policy, break; case Type: { + PrintingPolicy SubPolicy(Policy); + SubPolicy.SuppressStrongLifetime = true; std::string TypeStr; - getAsType().getAsStringInternal(TypeStr, Policy); + getAsType().getAsStringInternal(TypeStr, SubPolicy); Out << TypeStr; break; } diff --git a/lib/AST/TemplateName.cpp b/lib/AST/TemplateName.cpp index ebd07f486783..1f7b19aae471 100644 --- a/lib/AST/TemplateName.cpp +++ b/lib/AST/TemplateName.cpp @@ -27,7 +27,19 @@ SubstTemplateTemplateParmPackStorage::getArgumentPack() const { return TemplateArgument(Arguments, size()); } -void SubstTemplateTemplateParmPackStorage::Profile(llvm::FoldingSetNodeID &ID) { +void SubstTemplateTemplateParmStorage::Profile(llvm::FoldingSetNodeID &ID) { + Profile(ID, Parameter, Replacement); +} + +void SubstTemplateTemplateParmStorage::Profile(llvm::FoldingSetNodeID &ID, + TemplateTemplateParmDecl *parameter, + TemplateName replacement) { + ID.AddPointer(parameter); + ID.AddPointer(replacement.getAsVoidPointer()); +} + +void SubstTemplateTemplateParmPackStorage::Profile(llvm::FoldingSetNodeID &ID, + ASTContext &Context) { Profile(ID, Context, Parameter, TemplateArgument(Arguments, size())); } @@ -46,9 +58,14 @@ TemplateName::NameKind TemplateName::getKind() const { return DependentTemplate; if (Storage.is<QualifiedTemplateName *>()) return QualifiedTemplate; - - return getAsOverloadedTemplate()? OverloadedTemplate - : SubstTemplateTemplateParmPack; + + UncommonTemplateNameStorage *uncommon + = Storage.get<UncommonTemplateNameStorage*>(); + if (uncommon->getAsOverloadedStorage()) + return OverloadedTemplate; + if (uncommon->getAsSubstTemplateTemplateParm()) + return SubstTemplateTemplateParm; + return SubstTemplateTemplateParmPack; } TemplateDecl *TemplateName::getAsTemplateDecl() const { @@ -58,6 +75,9 @@ TemplateDecl *TemplateName::getAsTemplateDecl() const { if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) return QTN->getTemplateDecl(); + if (SubstTemplateTemplateParmStorage *sub = getAsSubstTemplateTemplateParm()) + return sub->getReplacement().getAsTemplateDecl(); + return 0; } @@ -79,6 +99,15 @@ bool TemplateName::isDependent() const { return true; } +bool TemplateName::isInstantiationDependent() const { + if (QualifiedTemplateName *QTN = getAsQualifiedTemplateName()) { + if (QTN->getQualifier()->isInstantiationDependent()) + return true; + } + + return isDependent(); +} + bool TemplateName::containsUnexpandedParameterPack() const { if (TemplateDecl *Template = getAsTemplateDecl()) { if (TemplateTemplateParmDecl *TTP @@ -115,6 +144,9 @@ TemplateName::print(llvm::raw_ostream &OS, const PrintingPolicy &Policy, OS << DTN->getIdentifier()->getName(); else OS << "operator " << getOperatorSpelling(DTN->getOperator()); + } else if (SubstTemplateTemplateParmStorage *subst + = getAsSubstTemplateTemplateParm()) { + subst->getReplacement().print(OS, Policy, SuppressNNS); } else if (SubstTemplateTemplateParmPackStorage *SubstPack = getAsSubstTemplateTemplateParmPack()) OS << SubstPack->getParameterPack()->getNameAsString(); diff --git a/lib/AST/Type.cpp b/lib/AST/Type.cpp index d28755284477..08971eb03421 100644 --- a/lib/AST/Type.cpp +++ b/lib/AST/Type.cpp @@ -36,7 +36,10 @@ bool Qualifiers::isStrictSupersetOf(Qualifiers Other) const { (hasObjCGCAttr() && !Other.hasObjCGCAttr())) && // Address space superset. ((getAddressSpace() == Other.getAddressSpace()) || - (hasAddressSpace()&& !Other.hasAddressSpace())); + (hasAddressSpace()&& !Other.hasAddressSpace())) && + // Lifetime qualifier superset. + ((getObjCLifetime() == Other.getObjCLifetime()) || + (hasObjCLifetime() && !Other.hasObjCLifetime())); } bool QualType::isConstant(QualType T, ASTContext &Ctx) { @@ -107,6 +110,7 @@ DependentSizedExtVectorType::DependentSizedExtVectorType(const Expr *SizeExpr, SourceLocation loc) : Type(DependentSizedExtVector, can, /*Dependent=*/true, + /*InstantiationDependent=*/true, ElementType->isVariablyModifiedType(), (ElementType->containsUnexpandedParameterPack() || (SizeExpr && SizeExpr->containsUnexpandedParameterPack()))), @@ -126,6 +130,7 @@ DependentSizedExtVectorType::Profile(llvm::FoldingSetNodeID &ID, VectorType::VectorType(QualType vecType, unsigned nElements, QualType canonType, VectorKind vecKind) : Type(Vector, canonType, vecType->isDependentType(), + vecType->isInstantiationDependentType(), vecType->isVariablyModifiedType(), vecType->containsUnexpandedParameterPack()), ElementType(vecType) @@ -137,6 +142,7 @@ VectorType::VectorType(QualType vecType, unsigned nElements, QualType canonType, VectorType::VectorType(TypeClass tc, QualType vecType, unsigned nElements, QualType canonType, VectorKind vecKind) : Type(tc, canonType, vecType->isDependentType(), + vecType->isInstantiationDependentType(), vecType->isVariablyModifiedType(), vecType->containsUnexpandedParameterPack()), ElementType(vecType) @@ -174,6 +180,26 @@ QualType QualType::getDesugaredType(QualType T, const ASTContext &Context) { return Context.getQualifiedType(split.first, split.second); } +QualType QualType::getSingleStepDesugaredType(const ASTContext &Context) const { + QualifierCollector Qs; + + const Type *CurTy = Qs.strip(*this); + switch (CurTy->getTypeClass()) { +#define ABSTRACT_TYPE(Class, Parent) +#define TYPE(Class, Parent) \ + case Type::Class: { \ + const Class##Type *Ty = cast<Class##Type>(CurTy); \ + if (!Ty->isSugared()) \ + return *this; \ + return Context.getQualifiedType(Ty->desugar(), Qs); \ + break; \ + } +#include "clang/AST/TypeNodes.def" + } + + return *this; +} + SplitQualType QualType::getSplitDesugaredType(QualType T) { QualifierCollector Qs; @@ -285,7 +311,6 @@ bool Type::isDerivedType() const { return false; } } - bool Type::isClassType() const { if (const RecordType *RT = getAs<RecordType>()) return RT->getDecl()->isClass(); @@ -385,7 +410,7 @@ const RecordType *Type::getAsUnionType() const { ObjCObjectType::ObjCObjectType(QualType Canonical, QualType Base, ObjCProtocolDecl * const *Protocols, unsigned NumProtocols) - : Type(ObjCObject, Canonical, false, false, false), + : Type(ObjCObject, Canonical, false, false, false, false), BaseType(Base) { ObjCObjectTypeBits.NumProtocols = NumProtocols; @@ -866,39 +891,57 @@ bool Type::isIncompleteType() const { } } -/// isPODType - Return true if this is a plain-old-data type (C++ 3.9p10) -bool Type::isPODType() const { +bool QualType::isPODType(ASTContext &Context) const { // The compiler shouldn't query this for incomplete types, but the user might. // We return false for that case. Except for incomplete arrays of PODs, which // are PODs according to the standard. - if (isIncompleteArrayType() && - cast<ArrayType>(CanonicalType)->getElementType()->isPODType()) - return true; - if (isIncompleteType()) + if (isNull()) + return 0; + + if ((*this)->isIncompleteArrayType()) + return Context.getBaseElementType(*this).isPODType(Context); + + if ((*this)->isIncompleteType()) return false; + if (Context.getLangOptions().ObjCAutoRefCount) { + switch (getObjCLifetime()) { + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_None: + break; + } + } + + QualType CanonicalType = getTypePtr()->CanonicalType; switch (CanonicalType->getTypeClass()) { // Everything not explicitly mentioned is not POD. default: return false; - case VariableArray: - case ConstantArray: + case Type::VariableArray: + case Type::ConstantArray: // IncompleteArray is handled above. - return cast<ArrayType>(CanonicalType)->getElementType()->isPODType(); - - case Builtin: - case Complex: - case Pointer: - case MemberPointer: - case Vector: - case ExtVector: - case ObjCObjectPointer: - case BlockPointer: + return Context.getBaseElementType(*this).isPODType(Context); + + case Type::ObjCObjectPointer: + case Type::BlockPointer: + case Type::Builtin: + case Type::Complex: + case Type::Pointer: + case Type::MemberPointer: + case Type::Vector: + case Type::ExtVector: return true; - case Enum: + case Type::Enum: return true; - case Record: + case Type::Record: if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(cast<RecordType>(CanonicalType)->getDecl())) return ClassDecl->isPOD(); @@ -908,6 +951,121 @@ bool Type::isPODType() const { } } +bool QualType::isTrivialType(ASTContext &Context) const { + // The compiler shouldn't query this for incomplete types, but the user might. + // We return false for that case. Except for incomplete arrays of PODs, which + // are PODs according to the standard. + if (isNull()) + return 0; + + if ((*this)->isArrayType()) + return Context.getBaseElementType(*this).isTrivialType(Context); + + // Return false for incomplete types after skipping any incomplete array + // types which are expressly allowed by the standard and thus our API. + if ((*this)->isIncompleteType()) + return false; + + if (Context.getLangOptions().ObjCAutoRefCount) { + switch (getObjCLifetime()) { + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_None: + if ((*this)->isObjCLifetimeType()) + return false; + break; + } + } + + QualType CanonicalType = getTypePtr()->CanonicalType; + if (CanonicalType->isDependentType()) + return false; + + // C++0x [basic.types]p9: + // Scalar types, trivial class types, arrays of such types, and + // cv-qualified versions of these types are collectively called trivial + // types. + + // As an extension, Clang treats vector types as Scalar types. + if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) + return true; + if (const RecordType *RT = CanonicalType->getAs<RecordType>()) { + if (const CXXRecordDecl *ClassDecl = + dyn_cast<CXXRecordDecl>(RT->getDecl())) { + // C++0x [class]p5: + // A trivial class is a class that has a trivial default constructor + if (!ClassDecl->hasTrivialDefaultConstructor()) return false; + // and is trivially copyable. + if (!ClassDecl->isTriviallyCopyable()) return false; + } + + return true; + } + + // No other types can match. + return false; +} + +bool QualType::isTriviallyCopyableType(ASTContext &Context) const { + if ((*this)->isArrayType()) + return Context.getBaseElementType(*this).isTrivialType(Context); + + if (Context.getLangOptions().ObjCAutoRefCount) { + switch (getObjCLifetime()) { + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_None: + if ((*this)->isObjCLifetimeType()) + return false; + break; + } + } + + // C++0x [basic.types]p9 + // Scalar types, trivially copyable class types, arrays of such types, and + // cv-qualified versions of these types are collectively called trivial + // types. + + QualType CanonicalType = getCanonicalType(); + if (CanonicalType->isDependentType()) + return false; + + // Return false for incomplete types after skipping any incomplete array types + // which are expressly allowed by the standard and thus our API. + if (CanonicalType->isIncompleteType()) + return false; + + // As an extension, Clang treats vector types as Scalar types. + if (CanonicalType->isScalarType() || CanonicalType->isVectorType()) + return true; + + if (const RecordType *RT = CanonicalType->getAs<RecordType>()) { + if (const CXXRecordDecl *ClassDecl = + dyn_cast<CXXRecordDecl>(RT->getDecl())) { + if (!ClassDecl->isTriviallyCopyable()) return false; + } + + return true; + } + + // No other types can match. + return false; +} + + + bool Type::isLiteralType() const { if (isDependentType()) return false; @@ -928,6 +1086,10 @@ bool Type::isLiteralType() const { if (BaseTy->isIncompleteType()) return false; + // Objective-C lifetime types are not literal types. + if (BaseTy->isObjCRetainableType()) + return false; + // C++0x [basic.types]p10: // A type is a literal type if it is: // -- a scalar type; or @@ -961,68 +1123,6 @@ bool Type::isLiteralType() const { return false; } -bool Type::isTrivialType() const { - if (isDependentType()) - return false; - - // C++0x [basic.types]p9: - // Scalar types, trivial class types, arrays of such types, and - // cv-qualified versions of these types are collectively called trivial - // types. - const Type *BaseTy = getBaseElementTypeUnsafe(); - assert(BaseTy && "NULL element type"); - - // Return false for incomplete types after skipping any incomplete array - // types which are expressly allowed by the standard and thus our API. - if (BaseTy->isIncompleteType()) - return false; - - // As an extension, Clang treats vector types as Scalar types. - if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true; - if (const RecordType *RT = BaseTy->getAs<RecordType>()) { - if (const CXXRecordDecl *ClassDecl = - dyn_cast<CXXRecordDecl>(RT->getDecl())) { - if (!ClassDecl->isTrivial()) return false; - } - - return true; - } - - // No other types can match. - return false; -} - -bool Type::isTriviallyCopyableType() const { - if (isDependentType()) - return false; - - // C++0x [basic.types]p9 - // Scalar types, trivially copyable class types, arrays of such types, and - // cv-qualified versions of these types are collectively called trivial - // types. - const Type *BaseTy = getBaseElementTypeUnsafe(); - assert(BaseTy && "NULL element type"); - - // Return false for incomplete types after skipping any incomplete array types - // which are expressly allowed by the standard and thus our API. - if (BaseTy->isIncompleteType()) - return false; - - // As an extension, Clang treats vector types as Scalar types. - if (BaseTy->isScalarType() || BaseTy->isVectorType()) return true; - if (const RecordType *RT = BaseTy->getAs<RecordType>()) { - if (const CXXRecordDecl *ClassDecl = - dyn_cast<CXXRecordDecl>(RT->getDecl())) { - if (!ClassDecl->isTriviallyCopyable()) return false; - } - - return true; - } - - // No other types can match. - return false; -} - bool Type::isStandardLayoutType() const { if (isDependentType()) return false; @@ -1060,14 +1160,32 @@ bool Type::isStandardLayoutType() const { // This is effectively the intersection of isTrivialType and // isStandardLayoutType. We implement it dircetly to avoid redundant // conversions from a type to a CXXRecordDecl. -bool Type::isCXX11PODType() const { - if (isDependentType()) +bool QualType::isCXX11PODType(ASTContext &Context) const { + const Type *ty = getTypePtr(); + if (ty->isDependentType()) return false; + if (Context.getLangOptions().ObjCAutoRefCount) { + switch (getObjCLifetime()) { + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_None: + if (ty->isObjCLifetimeType()) + return false; + break; + } + } + // C++11 [basic.types]p9: // Scalar types, POD classes, arrays of such types, and cv-qualified // versions of these types are collectively called trivial types. - const Type *BaseTy = getBaseElementTypeUnsafe(); + const Type *BaseTy = ty->getBaseElementTypeUnsafe(); assert(BaseTy && "NULL element type"); // Return false for incomplete types after skipping any incomplete array @@ -1253,7 +1371,7 @@ DependentTemplateSpecializationType::DependentTemplateSpecializationType( NestedNameSpecifier *NNS, const IdentifierInfo *Name, unsigned NumArgs, const TemplateArgument *Args, QualType Canon) - : TypeWithKeyword(Keyword, DependentTemplateSpecialization, Canon, true, + : TypeWithKeyword(Keyword, DependentTemplateSpecialization, Canon, true, true, /*VariablyModified=*/false, NNS && NNS->containsUnexpandedParameterPack()), NNS(NNS), Name(Name), NumArgs(NumArgs) { @@ -1388,18 +1506,22 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, : FunctionType(FunctionProto, result, epi.Variadic, epi.TypeQuals, epi.RefQualifier, canonical, result->isDependentType(), + result->isInstantiationDependentType(), result->isVariablyModifiedType(), result->containsUnexpandedParameterPack(), epi.ExtInfo), NumArgs(numArgs), NumExceptions(epi.NumExceptions), - ExceptionSpecType(epi.ExceptionSpecType) + ExceptionSpecType(epi.ExceptionSpecType), + HasAnyConsumedArgs(epi.ConsumedArguments != 0) { // Fill in the trailing argument array. QualType *argSlot = reinterpret_cast<QualType*>(this+1); for (unsigned i = 0; i != numArgs; ++i) { if (args[i]->isDependentType()) setDependent(); - + else if (args[i]->isInstantiationDependentType()) + setInstantiationDependent(); + if (args[i]->containsUnexpandedParameterPack()) setContainsUnexpandedParameterPack(); @@ -1412,7 +1534,9 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, for (unsigned i = 0, e = epi.NumExceptions; i != e; ++i) { if (epi.Exceptions[i]->isDependentType()) setDependent(); - + else if (epi.Exceptions[i]->isInstantiationDependentType()) + setInstantiationDependent(); + if (epi.Exceptions[i]->containsUnexpandedParameterPack()) setContainsUnexpandedParameterPack(); @@ -1422,6 +1546,20 @@ FunctionProtoType::FunctionProtoType(QualType result, const QualType *args, // Store the noexcept expression and context. Expr **noexSlot = reinterpret_cast<Expr**>(argSlot + numArgs); *noexSlot = epi.NoexceptExpr; + + if (epi.NoexceptExpr) { + if (epi.NoexceptExpr->isValueDependent() + || epi.NoexceptExpr->isTypeDependent()) + setDependent(); + else if (epi.NoexceptExpr->isInstantiationDependent()) + setInstantiationDependent(); + } + } + + if (epi.ConsumedArguments) { + bool *consumedArgs = const_cast<bool*>(getConsumedArgsBuffer()); + for (unsigned i = 0; i != numArgs; ++i) + consumedArgs[i] = epi.ConsumedArguments[i]; } } @@ -1461,18 +1599,48 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result, const QualType *ArgTys, unsigned NumArgs, const ExtProtoInfo &epi, const ASTContext &Context) { + + // We have to be careful not to get ambiguous profile encodings. + // Note that valid type pointers are never ambiguous with anything else. + // + // The encoding grammar begins: + // type type* bool int bool + // If that final bool is true, then there is a section for the EH spec: + // bool type* + // This is followed by an optional "consumed argument" section of the + // same length as the first type sequence: + // bool* + // Finally, we have the ext info: + // int + // + // There is no ambiguity between the consumed arguments and an empty EH + // spec because of the leading 'bool' which unambiguously indicates + // whether the following bool is the EH spec or part of the arguments. + ID.AddPointer(Result.getAsOpaquePtr()); for (unsigned i = 0; i != NumArgs; ++i) ID.AddPointer(ArgTys[i].getAsOpaquePtr()); - ID.AddBoolean(epi.Variadic); - ID.AddInteger(epi.TypeQuals); - ID.AddInteger(epi.RefQualifier); - ID.AddInteger(epi.ExceptionSpecType); + // This method is relatively performance sensitive, so as a performance + // shortcut, use one AddInteger call instead of four for the next four + // fields. + assert(!(unsigned(epi.Variadic) & ~1) && + !(unsigned(epi.TypeQuals) & ~255) && + !(unsigned(epi.RefQualifier) & ~3) && + !(unsigned(epi.ExceptionSpecType) & ~7) && + "Values larger than expected."); + ID.AddInteger(unsigned(epi.Variadic) + + (epi.TypeQuals << 1) + + (epi.RefQualifier << 9) + + (epi.ExceptionSpecType << 11)); if (epi.ExceptionSpecType == EST_Dynamic) { for (unsigned i = 0; i != epi.NumExceptions; ++i) ID.AddPointer(epi.Exceptions[i].getAsOpaquePtr()); } else if (epi.ExceptionSpecType == EST_ComputedNoexcept && epi.NoexceptExpr){ - epi.NoexceptExpr->Profile(ID, Context, true); + epi.NoexceptExpr->Profile(ID, Context, false); + } + if (epi.ConsumedArguments) { + for (unsigned i = 0; i != NumArgs; ++i) + ID.AddBoolean(epi.ConsumedArguments[i]); } epi.ExtInfo.Profile(ID); } @@ -1489,13 +1657,21 @@ QualType TypedefType::desugar() const { TypeOfExprType::TypeOfExprType(Expr *E, QualType can) : Type(TypeOfExpr, can, E->isTypeDependent(), + E->isInstantiationDependent(), E->getType()->isVariablyModifiedType(), E->containsUnexpandedParameterPack()), TOExpr(E) { } +bool TypeOfExprType::isSugared() const { + return !TOExpr->isTypeDependent(); +} + QualType TypeOfExprType::desugar() const { - return getUnderlyingExpr()->getType(); + if (isSugared()) + return getUnderlyingExpr()->getType(); + + return QualType(this, 0); } void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID, @@ -1505,12 +1681,22 @@ void DependentTypeOfExprType::Profile(llvm::FoldingSetNodeID &ID, DecltypeType::DecltypeType(Expr *E, QualType underlyingType, QualType can) : Type(Decltype, can, E->isTypeDependent(), + E->isInstantiationDependent(), E->getType()->isVariablyModifiedType(), E->containsUnexpandedParameterPack()), E(E), UnderlyingType(underlyingType) { } +bool DecltypeType::isSugared() const { return !E->isInstantiationDependent(); } + +QualType DecltypeType::desugar() const { + if (isSugared()) + return getUnderlyingType(); + + return QualType(this, 0); +} + DependentDecltypeType::DependentDecltypeType(const ASTContext &Context, Expr *E) : DecltypeType(E, Context.DependentTy), Context(Context) { } @@ -1520,7 +1706,9 @@ void DependentDecltypeType::Profile(llvm::FoldingSetNodeID &ID, } TagType::TagType(TypeClass TC, const TagDecl *D, QualType can) - : Type(TC, can, D->isDependentType(), /*VariablyModified=*/false, + : Type(TC, can, D->isDependentType(), + /*InstantiationDependent=*/D->isDependentType(), + /*VariablyModified=*/false, /*ContainsUnexpandedParameterPack=*/false), decl(const_cast<TagDecl*>(D)) {} @@ -1540,6 +1728,7 @@ UnaryTransformType::UnaryTransformType(QualType BaseType, UTTKind UKind, QualType CanonicalType) : Type(UnaryTransform, CanonicalType, UnderlyingType->isDependentType(), + UnderlyingType->isInstantiationDependentType(), UnderlyingType->isVariablyModifiedType(), BaseType->containsUnexpandedParameterPack()) , BaseType(BaseType), UnderlyingType(UnderlyingType), UKind(UKind) @@ -1573,7 +1762,8 @@ SubstTemplateTypeParmPackType:: SubstTemplateTypeParmPackType(const TemplateTypeParmType *Param, QualType Canon, const TemplateArgument &ArgPack) - : Type(SubstTemplateTypeParmPack, Canon, true, false, true), Replaced(Param), + : Type(SubstTemplateTypeParmPack, Canon, true, true, false, true), + Replaced(Param), Arguments(ArgPack.pack_begin()), NumArguments(ArgPack.pack_size()) { } @@ -1598,23 +1788,39 @@ void SubstTemplateTypeParmPackType::Profile(llvm::FoldingSetNodeID &ID, } bool TemplateSpecializationType:: -anyDependentTemplateArguments(const TemplateArgumentListInfo &Args) { - return anyDependentTemplateArguments(Args.getArgumentArray(), Args.size()); +anyDependentTemplateArguments(const TemplateArgumentListInfo &Args, + bool &InstantiationDependent) { + return anyDependentTemplateArguments(Args.getArgumentArray(), Args.size(), + InstantiationDependent); } bool TemplateSpecializationType:: -anyDependentTemplateArguments(const TemplateArgumentLoc *Args, unsigned N) { - for (unsigned i = 0; i != N; ++i) - if (Args[i].getArgument().isDependent()) +anyDependentTemplateArguments(const TemplateArgumentLoc *Args, unsigned N, + bool &InstantiationDependent) { + for (unsigned i = 0; i != N; ++i) { + if (Args[i].getArgument().isDependent()) { + InstantiationDependent = true; return true; + } + + if (Args[i].getArgument().isInstantiationDependent()) + InstantiationDependent = true; + } return false; } bool TemplateSpecializationType:: -anyDependentTemplateArguments(const TemplateArgument *Args, unsigned N) { - for (unsigned i = 0; i != N; ++i) - if (Args[i].isDependent()) +anyDependentTemplateArguments(const TemplateArgument *Args, unsigned N, + bool &InstantiationDependent) { + for (unsigned i = 0; i != N; ++i) { + if (Args[i].isDependent()) { + InstantiationDependent = true; return true; + } + + if (Args[i].isInstantiationDependent()) + InstantiationDependent = true; + } return false; } @@ -1625,12 +1831,22 @@ TemplateSpecializationType(TemplateName T, : Type(TemplateSpecialization, Canon.isNull()? QualType(this, 0) : Canon, Canon.isNull()? T.isDependent() : Canon->isDependentType(), + Canon.isNull()? T.isDependent() + : Canon->isInstantiationDependentType(), false, T.containsUnexpandedParameterPack()), Template(T), NumArgs(NumArgs) { assert(!T.getAsDependentTemplateName() && "Use DependentTemplateSpecializationType for dependent template-name"); + assert((T.getKind() == TemplateName::Template || + T.getKind() == TemplateName::SubstTemplateTemplateParm || + T.getKind() == TemplateName::SubstTemplateTemplateParmPack) && + "Unexpected template name for TemplateSpecializationType"); + bool InstantiationDependent; + (void)InstantiationDependent; assert((!Canon.isNull() || - T.isDependent() || anyDependentTemplateArguments(Args, NumArgs)) && + T.isDependent() || + anyDependentTemplateArguments(Args, NumArgs, + InstantiationDependent)) && "No canonical type for non-dependent class template specialization"); TemplateArgument *TemplateArgs @@ -1644,6 +1860,9 @@ TemplateSpecializationType(TemplateName T, // U<T> is always non-dependent, irrespective of the type T. if (Canon.isNull() && Args[Arg].isDependent()) setDependent(); + else if (Args[Arg].isInstantiationDependent()) + setInstantiationDependent(); + if (Args[Arg].getKind() == TemplateArgument::Type && Args[Arg].getAsType()->isVariablyModifiedType()) setVariablyModified(); @@ -1795,8 +2014,8 @@ static CachedProperties computeCachedProperties(const Type *T) { #define DEPENDENT_TYPE(Class,Base) case Type::Class: #define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class,Base) case Type::Class: #include "clang/AST/TypeNodes.def" - // Treat dependent types as external. - assert(T->isDependentType()); + // Treat instantiation-dependent types as external. + assert(T->isInstantiationDependentType()); return CachedProperties(ExternalLinkage, DefaultVisibility, false); case Type::Builtin: @@ -1900,6 +2119,79 @@ void Type::ClearLinkageCache() { CanonicalType->TypeBits.CacheValidAndVisibility = 0; } +Qualifiers::ObjCLifetime Type::getObjCARCImplicitLifetime() const { + if (isObjCARCImplicitlyUnretainedType()) + return Qualifiers::OCL_ExplicitNone; + return Qualifiers::OCL_Strong; +} + +bool Type::isObjCARCImplicitlyUnretainedType() const { + assert(isObjCLifetimeType() && + "cannot query implicit lifetime for non-inferrable type"); + + const Type *canon = getCanonicalTypeInternal().getTypePtr(); + + // Walk down to the base type. We don't care about qualifiers for this. + while (const ArrayType *array = dyn_cast<ArrayType>(canon)) + canon = array->getElementType().getTypePtr(); + + if (const ObjCObjectPointerType *opt + = dyn_cast<ObjCObjectPointerType>(canon)) { + // Class and Class<Protocol> don't require retension. + if (opt->getObjectType()->isObjCClass()) + return true; + } + + return false; +} + +bool Type::isObjCNSObjectType() const { + if (const TypedefType *typedefType = dyn_cast<TypedefType>(this)) + return typedefType->getDecl()->hasAttr<ObjCNSObjectAttr>(); + return false; +} +bool Type::isObjCRetainableType() const { + return isObjCObjectPointerType() || + isBlockPointerType() || + isObjCNSObjectType(); +} +bool Type::isObjCIndirectLifetimeType() const { + if (isObjCLifetimeType()) + return true; + if (const PointerType *OPT = getAs<PointerType>()) + return OPT->getPointeeType()->isObjCIndirectLifetimeType(); + if (const ReferenceType *Ref = getAs<ReferenceType>()) + return Ref->getPointeeType()->isObjCIndirectLifetimeType(); + if (const MemberPointerType *MemPtr = getAs<MemberPointerType>()) + return MemPtr->getPointeeType()->isObjCIndirectLifetimeType(); + return false; +} + +/// Returns true if objects of this type have lifetime semantics under +/// ARC. +bool Type::isObjCLifetimeType() const { + const Type *type = this; + while (const ArrayType *array = type->getAsArrayTypeUnsafe()) + type = array->getElementType().getTypePtr(); + return type->isObjCRetainableType(); +} + +/// \brief Determine whether the given type T is a "bridgable" Objective-C type, +/// which is either an Objective-C object pointer type or an +bool Type::isObjCARCBridgableType() const { + return isObjCObjectPointerType() || isBlockPointerType(); +} + +/// \brief Determine whether the given type T is a "bridgeable" C type. +bool Type::isCARCBridgableType() const { + const PointerType *Pointer = getAs<PointerType>(); + if (!Pointer) + return false; + + QualType Pointee = Pointer->getPointeeType(); + return Pointee->isVoidType() || Pointee->isRecordType(); +} + bool Type::hasSizedVLAType() const { if (!isVariablyModifiedType()) return false; @@ -1919,6 +2211,18 @@ bool Type::hasSizedVLAType() const { } QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { + switch (type.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + return DK_objc_strong_lifetime; + case Qualifiers::OCL_Weak: + return DK_objc_weak_lifetime; + } + /// Currently, the only destruction kind we recognize is C++ objects /// with non-trivial destructors. const CXXRecordDecl *record = @@ -1928,3 +2232,24 @@ QualType::DestructionKind QualType::isDestructedTypeImpl(QualType type) { return DK_none; } + +bool QualType::hasTrivialCopyAssignment(ASTContext &Context) const { + switch (getObjCLifetime()) { + case Qualifiers::OCL_None: + break; + + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Autoreleasing: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return !Context.getLangOptions().ObjCAutoRefCount; + } + + if (const CXXRecordDecl *Record + = getTypePtr()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl()) + return Record->hasTrivialCopyAssignment(); + + return true; +} diff --git a/lib/AST/TypePrinter.cpp b/lib/AST/TypePrinter.cpp index 4519606f6e82..b89d2aa31676 100644 --- a/lib/AST/TypePrinter.cpp +++ b/lib/AST/TypePrinter.cpp @@ -24,6 +24,23 @@ using namespace clang; namespace { + /// \brief RAII object that enables printing of the ARC __strong lifetime + /// qualifier. + class IncludeStrongLifetimeRAII { + PrintingPolicy &Policy; + bool Old; + + public: + explicit IncludeStrongLifetimeRAII(PrintingPolicy &Policy) + : Policy(Policy), Old(Policy.SuppressStrongLifetime) { + Policy.SuppressStrongLifetime = false; + } + + ~IncludeStrongLifetimeRAII() { + Policy.SuppressStrongLifetime = Old; + } + }; + class TypePrinter { PrintingPolicy Policy; @@ -78,7 +95,7 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { // "int * const", printing "const int *" is different. Only do this when the // type expands to a simple string. bool CanPrefixQualifiers = false; - + bool NeedARCStrongQualifier = false; Type::TypeClass TC = T->getTypeClass(); if (const AutoType *AT = dyn_cast<AutoType>(T)) TC = AT->desugar()->getTypeClass(); @@ -114,15 +131,18 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { T->isObjCQualifiedIdType() || T->isObjCQualifiedClassType(); break; + case Type::ConstantArray: + case Type::IncompleteArray: + case Type::VariableArray: + case Type::DependentSizedArray: + NeedARCStrongQualifier = true; + // Fall through + case Type::Pointer: case Type::BlockPointer: case Type::LValueReference: case Type::RValueReference: case Type::MemberPointer: - case Type::ConstantArray: - case Type::IncompleteArray: - case Type::VariableArray: - case Type::DependentSizedArray: case Type::DependentSizedExtVector: case Type::Vector: case Type::ExtVector: @@ -139,13 +159,20 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { if (!CanPrefixQualifiers && !Quals.empty()) { std::string qualsBuffer; - Quals.getAsStringInternal(qualsBuffer, Policy); + if (NeedARCStrongQualifier) { + IncludeStrongLifetimeRAII Strong(Policy); + Quals.getAsStringInternal(qualsBuffer, Policy); + } else { + Quals.getAsStringInternal(qualsBuffer, Policy); + } - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; + if (!qualsBuffer.empty()) { + if (!buffer.empty()) { + qualsBuffer += ' '; + qualsBuffer += buffer; + } + std::swap(buffer, qualsBuffer); } - std::swap(buffer, qualsBuffer); } switch (T->getTypeClass()) { @@ -159,13 +186,20 @@ void TypePrinter::print(const Type *T, Qualifiers Quals, std::string &buffer) { // If we're adding the qualifiers as a prefix, do it now. if (CanPrefixQualifiers && !Quals.empty()) { std::string qualsBuffer; - Quals.getAsStringInternal(qualsBuffer, Policy); - - if (!buffer.empty()) { - qualsBuffer += ' '; - qualsBuffer += buffer; + if (NeedARCStrongQualifier) { + IncludeStrongLifetimeRAII Strong(Policy); + Quals.getAsStringInternal(qualsBuffer, Policy); + } else { + Quals.getAsStringInternal(qualsBuffer, Policy); + } + + if (!qualsBuffer.empty()) { + if (!buffer.empty()) { + qualsBuffer += ' '; + qualsBuffer += buffer; + } + std::swap(buffer, qualsBuffer); } - std::swap(buffer, qualsBuffer); } } @@ -192,6 +226,7 @@ void TypePrinter::printPointer(const PointerType *T, std::string &S) { if (isa<ArrayType>(T->getPointeeType())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeType(), S); } @@ -209,6 +244,7 @@ void TypePrinter::printLValueReference(const LValueReferenceType *T, if (isa<ArrayType>(T->getPointeeTypeAsWritten())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeTypeAsWritten(), S); } @@ -221,6 +257,7 @@ void TypePrinter::printRValueReference(const RValueReferenceType *T, if (isa<ArrayType>(T->getPointeeTypeAsWritten())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeTypeAsWritten(), S); } @@ -236,6 +273,7 @@ void TypePrinter::printMemberPointer(const MemberPointerType *T, if (isa<ArrayType>(T->getPointeeType())) S = '(' + S + ')'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getPointeeType(), S); } @@ -245,12 +283,14 @@ void TypePrinter::printConstantArray(const ConstantArrayType *T, S += llvm::utostr(T->getSize().getZExtValue()); S += ']'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getElementType(), S); } void TypePrinter::printIncompleteArray(const IncompleteArrayType *T, std::string &S) { S += "[]"; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getElementType(), S); } @@ -276,6 +316,7 @@ void TypePrinter::printVariableArray(const VariableArrayType *T, } S += ']'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getElementType(), S); } @@ -291,6 +332,7 @@ void TypePrinter::printDependentSizedArray(const DependentSizedArrayType *T, } S += ']'; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getElementType(), S); } @@ -518,6 +560,7 @@ void TypePrinter::printUnaryTransform(const UnaryTransformType *T, if (!S.empty()) S = ' ' + S; std::string Str; + IncludeStrongLifetimeRAII Strong(Policy); print(T->getBaseType(), Str); switch (T->getUTTKind()) { @@ -552,6 +595,7 @@ void TypePrinter::AppendScope(DeclContext *DC, std::string &Buffer) { Buffer += "<anonymous>"; } else if (ClassTemplateSpecializationDecl *Spec = dyn_cast<ClassTemplateSpecializationDecl>(DC)) { + IncludeStrongLifetimeRAII Strong(Policy); const TemplateArgumentList &TemplateArgs = Spec->getTemplateArgs(); std::string TemplateArgsStr = TemplateSpecializationType::PrintTemplateArgumentList( @@ -642,6 +686,7 @@ void TypePrinter::printTag(TagDecl *D, std::string &InnerString) { Args = TemplateArgs.data(); NumArgs = TemplateArgs.size(); } + IncludeStrongLifetimeRAII Strong(Policy); Buffer += TemplateSpecializationType::PrintTemplateArgumentList(Args, NumArgs, Policy); @@ -677,18 +722,21 @@ void TypePrinter::printTemplateTypeParm(const TemplateTypeParmType *T, void TypePrinter::printSubstTemplateTypeParm(const SubstTemplateTypeParmType *T, std::string &S) { + IncludeStrongLifetimeRAII Strong(Policy); print(T->getReplacementType(), S); } void TypePrinter::printSubstTemplateTypeParmPack( const SubstTemplateTypeParmPackType *T, std::string &S) { + IncludeStrongLifetimeRAII Strong(Policy); printTemplateTypeParm(T->getReplacedParameter(), S); } void TypePrinter::printTemplateSpecialization( const TemplateSpecializationType *T, std::string &S) { + IncludeStrongLifetimeRAII Strong(Policy); std::string SpecString; { @@ -765,6 +813,7 @@ void TypePrinter::printDependentName(const DependentNameType *T, std::string &S) void TypePrinter::printDependentTemplateSpecialization( const DependentTemplateSpecializationType *T, std::string &S) { + IncludeStrongLifetimeRAII Strong(Policy); std::string MyString; { llvm::raw_string_ostream OS(MyString); @@ -796,8 +845,9 @@ void TypePrinter::printPackExpansion(const PackExpansionType *T, void TypePrinter::printAttributed(const AttributedType *T, std::string &S) { - // Prefer the macro forms of the GC qualifiers. - if (T->getAttrKind() == AttributedType::attr_objc_gc) + // Prefer the macro forms of the GC and ownership qualifiers. + if (T->getAttrKind() == AttributedType::attr_objc_gc || + T->getAttrKind() == AttributedType::attr_objc_ownership) return print(T->getEquivalentType(), S); print(T->getModifiedType(), S); @@ -866,6 +916,18 @@ void TypePrinter::printAttributed(const AttributedType *T, break; } + case AttributedType::attr_objc_ownership: + S += "objc_ownership("; + switch (T->getEquivalentType().getObjCLifetime()) { + case Qualifiers::OCL_None: llvm_unreachable("no ownership!"); break; + case Qualifiers::OCL_ExplicitNone: S += "none"; break; + case Qualifiers::OCL_Strong: S += "strong"; break; + case Qualifiers::OCL_Weak: S += "weak"; break; + case Qualifiers::OCL_Autoreleasing: S += "autoreleasing"; break; + } + S += ")"; + break; + case AttributedType::attr_noreturn: S += "noreturn"; break; case AttributedType::attr_cdecl: S += "cdecl"; break; case AttributedType::attr_fastcall: S += "fastcall"; break; @@ -1080,7 +1142,7 @@ std::string Qualifiers::getAsString() const { // prefix a space if the string is non-empty. Will not append a final // space. void Qualifiers::getAsStringInternal(std::string &S, - const PrintingPolicy&) const { + const PrintingPolicy& Policy) const { AppendTypeQualList(S, getCVRQualifiers()); if (unsigned addrspace = getAddressSpace()) { if (!S.empty()) S += ' '; @@ -1095,6 +1157,23 @@ void Qualifiers::getAsStringInternal(std::string &S, else S += "__strong"; } + if (Qualifiers::ObjCLifetime lifetime = getObjCLifetime()) { + if (!S.empty() && + !(lifetime == Qualifiers::OCL_Strong && Policy.SuppressStrongLifetime)) + S += ' '; + + switch (lifetime) { + case Qualifiers::OCL_None: llvm_unreachable("none but true"); + case Qualifiers::OCL_ExplicitNone: S += "__unsafe_unretained"; break; + case Qualifiers::OCL_Strong: + if (!Policy.SuppressStrongLifetime) + S += "__strong"; + break; + + case Qualifiers::OCL_Weak: S += "__weak"; break; + case Qualifiers::OCL_Autoreleasing: S += "__autoreleasing"; break; + } + } } std::string QualType::getAsString(const Type *ty, Qualifiers qs) { diff --git a/lib/Analysis/CFG.cpp b/lib/Analysis/CFG.cpp index 3e540203eaa0..f231c147f11e 100644 --- a/lib/Analysis/CFG.cpp +++ b/lib/Analysis/CFG.cpp @@ -776,7 +776,7 @@ LocalScope* CFGBuilder::addLocalScopeForVarDecl(VarDecl* VD, QT = RT->getPointeeType(); if (!QT.isConstQualified()) return Scope; - if (!VD->getInit() || !VD->getInit()->Classify(*Context).isRValue()) + if (!VD->extendsLifetimeOfTemporary()) return Scope; } @@ -2763,6 +2763,10 @@ tryAgain: case Stmt::ParenExprClass: E = cast<ParenExpr>(E)->getSubExpr(); goto tryAgain; + + case Stmt::MaterializeTemporaryExprClass: + E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(); + goto tryAgain; } } diff --git a/lib/Analysis/CocoaConventions.cpp b/lib/Analysis/CocoaConventions.cpp index 946c38c875d5..90f7092f90ee 100644 --- a/lib/Analysis/CocoaConventions.cpp +++ b/lib/Analysis/CocoaConventions.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// // -// This file defines +// This file implements cocoa naming convention analysis. // //===----------------------------------------------------------------------===// @@ -36,8 +36,10 @@ using llvm::StringRef; // not release it." // -cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) { - switch (S.getMethodFamily()) { +cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S, + const ObjCMethodDecl *MD) { + switch (MD && MD->hasAttr<ObjCMethodFamilyAttr>()? MD->getMethodFamily() + : S.getMethodFamily()) { case OMF_None: case OMF_autorelease: case OMF_dealloc: @@ -45,6 +47,7 @@ cocoa::NamingConvention cocoa::deriveNamingConvention(Selector S) { case OMF_retain: case OMF_retainCount: case OMF_self: + case OMF_performSelector: return NoConvention; case OMF_init: @@ -83,12 +86,12 @@ bool cocoa::isRefType(QualType RetTy, llvm::StringRef Prefix, return Name.startswith(Prefix); } -bool cocoa::isCFObjectRef(QualType T) { - return isRefType(T, "CF") || // Core Foundation. - isRefType(T, "CG") || // Core Graphics. - isRefType(T, "DADisk") || // Disk Arbitration API. - isRefType(T, "DADissenter") || - isRefType(T, "DASessionRef"); +bool coreFoundation::isCFObjectRef(QualType T) { + return cocoa::isRefType(T, "CF") || // Core Foundation. + cocoa::isRefType(T, "CG") || // Core Graphics. + cocoa::isRefType(T, "DADisk") || // Disk Arbitration API. + cocoa::isRefType(T, "DADissenter") || + cocoa::isRefType(T, "DASessionRef"); } @@ -123,3 +126,47 @@ bool cocoa::isCocoaObjectRef(QualType Ty) { return false; } + +bool coreFoundation::followsCreateRule(llvm::StringRef functionName) { + llvm::StringRef::iterator it = functionName.begin(); + llvm::StringRef::iterator start = it; + llvm::StringRef::iterator endI = functionName.end(); + + while (true) { + // Scan for the start of 'create' or 'copy'. + for ( ; it != endI ; ++it) { + // Search for the first character. It can either be 'C' or 'c'. + char ch = *it; + if (ch == 'C' || ch == 'c') { + ++it; + break; + } + } + + // Did we hit the end of the string? If so, we didn't find a match. + if (it == endI) + return false; + + // Scan for *lowercase* 'reate' or 'opy', followed by no lowercase + // character. + llvm::StringRef suffix = functionName.substr(it - start); + if (suffix.startswith("reate")) { + it += 5; + } + else if (suffix.startswith("opy")) { + it += 3; + } + else { + // Keep scanning. + continue; + } + + if (it == endI || !islower(*it)) + return true; + + // If we matched a lowercase character, it isn't the end of the + // word. Keep scanning. + } + + return false; +} diff --git a/lib/Analysis/FormatString.cpp b/lib/Analysis/FormatString.cpp index c1b5ea8a652a..5f3cd4c61549 100644 --- a/lib/Analysis/FormatString.cpp +++ b/lib/Analysis/FormatString.cpp @@ -219,6 +219,7 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { argTy = C.getCanonicalType(argTy).getUnqualifiedType(); if (T == argTy) return true; + // Check for "compatible types". if (const BuiltinType *BT = argTy->getAs<BuiltinType>()) switch (BT->getKind()) { default: @@ -227,7 +228,7 @@ bool ArgTypeResult::matchesType(ASTContext &C, QualType argTy) const { case BuiltinType::SChar: return T == C.UnsignedCharTy; case BuiltinType::Char_U: - case BuiltinType::UChar: + case BuiltinType::UChar: return T == C.SignedCharTy; case BuiltinType::Short: return T == C.UnsignedShortTy; diff --git a/lib/Analysis/ReachableCode.cpp b/lib/Analysis/ReachableCode.cpp index 9ac456f53a67..c5b17fc77bb2 100644 --- a/lib/Analysis/ReachableCode.cpp +++ b/lib/Analysis/ReachableCode.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/SmallVector.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/StmtCXX.h" #include "clang/Analysis/Analyses/ReachableCode.h" #include "clang/Analysis/CFG.h" @@ -108,6 +109,11 @@ static SourceLocation GetUnreachableLoc(const CFGBlock &b, SourceRange &R1, case Stmt::CXXTryStmtClass: { return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc(); } + case Expr::ObjCBridgedCastExprClass: { + const ObjCBridgedCastExpr *CSC = cast<ObjCBridgedCastExpr>(S); + R1 = CSC->getSubExpr()->getSourceRange(); + return CSC->getLParenLoc(); + } default: ; } R1 = S->getSourceRange(); diff --git a/lib/Analysis/UninitializedValues.cpp b/lib/Analysis/UninitializedValues.cpp index e80e282813af..1d6959d81b16 100644 --- a/lib/Analysis/UninitializedValues.cpp +++ b/lib/Analysis/UninitializedValues.cpp @@ -288,28 +288,28 @@ class DataflowWorklist { public: DataflowWorklist(const CFG &cfg) : enqueuedBlocks(cfg.getNumBlockIDs()) {} - void enqueue(const CFGBlock *block); void enqueueSuccessors(const CFGBlock *block); const CFGBlock *dequeue(); - }; } -void DataflowWorklist::enqueue(const CFGBlock *block) { - if (!block) - return; - unsigned idx = block->getBlockID(); - if (enqueuedBlocks[idx]) - return; - worklist.push_back(block); - enqueuedBlocks[idx] = true; -} - void DataflowWorklist::enqueueSuccessors(const clang::CFGBlock *block) { + unsigned OldWorklistSize = worklist.size(); for (CFGBlock::const_succ_iterator I = block->succ_begin(), E = block->succ_end(); I != E; ++I) { - enqueue(*I); + const CFGBlock *Successor = *I; + if (!Successor || enqueuedBlocks[Successor->getBlockID()]) + continue; + worklist.push_back(Successor); + enqueuedBlocks[Successor->getBlockID()] = true; } + if (OldWorklistSize == 0 || OldWorklistSize == worklist.size()) + return; + + // Rotate the newly added blocks to the start of the worklist so that it forms + // a proper queue when we pop off the end of the worklist. + std::rotate(worklist.begin(), worklist.begin() + OldWorklistSize, + worklist.end()); } const CFGBlock *DataflowWorklist::dequeue() { @@ -654,15 +654,19 @@ static bool runOnBlock(const CFGBlock *block, const CFG &cfg, return vals.updateValueVectorWithScratch(block); } -void clang::runUninitializedVariablesAnalysis(const DeclContext &dc, - const CFG &cfg, - AnalysisContext &ac, - UninitVariablesHandler &handler) { +void clang::runUninitializedVariablesAnalysis( + const DeclContext &dc, + const CFG &cfg, + AnalysisContext &ac, + UninitVariablesHandler &handler, + UninitVariablesAnalysisStats &stats) { CFGBlockValues vals(cfg); vals.computeSetOfDeclarations(dc); if (vals.hasNoDeclarations()) return; + stats.NumVariablesAnalyzed = vals.getNumEntries(); + // Mark all variables uninitialized at the entry. const CFGBlock &entry = cfg.getEntry(); for (CFGBlock::const_succ_iterator i = entry.succ_begin(), @@ -684,7 +688,8 @@ void clang::runUninitializedVariablesAnalysis(const DeclContext &dc, while (const CFGBlock *block = worklist.dequeue()) { // Did the block change? - bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed); + bool changed = runOnBlock(block, cfg, ac, vals, wasAnalyzed); + ++stats.NumBlockVisits; if (changed || !previouslyVisited[block->getBlockID()]) worklist.enqueueSuccessors(block); previouslyVisited[block->getBlockID()] = true; @@ -692,11 +697,12 @@ void clang::runUninitializedVariablesAnalysis(const DeclContext &dc, // Run through the blocks one more time, and report uninitialized variabes. for (CFG::const_iterator BI = cfg.begin(), BE = cfg.end(); BI != BE; ++BI) { - if (wasAnalyzed[(*BI)->getBlockID()]) + if (wasAnalyzed[(*BI)->getBlockID()]) { runOnBlock(*BI, cfg, ac, vals, wasAnalyzed, &handler, /* flagBlockUses */ true); + ++stats.NumBlockVisits; + } } } UninitVariablesHandler::~UninitVariablesHandler() {} - diff --git a/lib/Basic/Builtins.cpp b/lib/Basic/Builtins.cpp index 845ae81517da..7df24a03b03a 100644 --- a/lib/Basic/Builtins.cpp +++ b/lib/Basic/Builtins.cpp @@ -18,10 +18,10 @@ using namespace clang; static const Builtin::Info BuiltinInfo[] = { - { "not a builtin function", 0, 0, 0, ALL_LANGUAGES, false }, -#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES, false }, + { "not a builtin function", 0, 0, 0, ALL_LANGUAGES }, +#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER, BUILTIN_LANG) { #ID, TYPE, ATTRS, HEADER,\ - BUILTIN_LANG, false }, + BUILTIN_LANG }, #include "clang/Basic/Builtins.def" }; @@ -46,8 +46,7 @@ void Builtin::Context::InitializeBuiltins(IdentifierTable &Table, const LangOptions& LangOpts) { // Step #1: mark all target-independent builtins with their ID's. for (unsigned i = Builtin::NotBuiltin+1; i != Builtin::FirstTSBuiltin; ++i) - if (!BuiltinInfo[i].Suppressed && - (!LangOpts.NoBuiltin || !strchr(BuiltinInfo[i].Attributes, 'f'))) { + if (!LangOpts.NoBuiltin || !strchr(BuiltinInfo[i].Attributes, 'f')) { if (LangOpts.ObjC1 || BuiltinInfo[i].builtin_lang != clang::OBJC_LANG) Table.get(BuiltinInfo[i].Name).setBuiltinID(i); @@ -55,10 +54,7 @@ void Builtin::Context::InitializeBuiltins(IdentifierTable &Table, // Step #2: Register target-specific builtins. for (unsigned i = 0, e = NumTSRecords; i != e; ++i) - if (!TSRecords[i].Suppressed && - (!LangOpts.NoBuiltin || - (TSRecords[i].Attributes && - !strchr(TSRecords[i].Attributes, 'f')))) + if (!LangOpts.NoBuiltin || !strchr(TSRecords[i].Attributes, 'f')) Table.get(TSRecords[i].Name).setBuiltinID(i+Builtin::FirstTSBuiltin); } @@ -67,16 +63,12 @@ Builtin::Context::GetBuiltinNames(llvm::SmallVectorImpl<const char *> &Names, bool NoBuiltins) { // Final all target-independent names for (unsigned i = Builtin::NotBuiltin+1; i != Builtin::FirstTSBuiltin; ++i) - if (!BuiltinInfo[i].Suppressed && - (!NoBuiltins || !strchr(BuiltinInfo[i].Attributes, 'f'))) + if (!NoBuiltins || !strchr(BuiltinInfo[i].Attributes, 'f')) Names.push_back(BuiltinInfo[i].Name); // Find target-specific names. for (unsigned i = 0, e = NumTSRecords; i != e; ++i) - if (!TSRecords[i].Suppressed && - (!NoBuiltins || - (TSRecords[i].Attributes && - !strchr(TSRecords[i].Attributes, 'f')))) + if (!NoBuiltins || !strchr(TSRecords[i].Attributes, 'f')) Names.push_back(TSRecords[i].Name); } diff --git a/lib/Basic/Diagnostic.cpp b/lib/Basic/Diagnostic.cpp index 11887ab0fe95..ae363a0df0d0 100644 --- a/lib/Basic/Diagnostic.cpp +++ b/lib/Basic/Diagnostic.cpp @@ -26,7 +26,8 @@ static void DummyArgToStringFn(Diagnostic::ArgumentKind AK, intptr_t QT, const Diagnostic::ArgumentValue *PrevArgs, unsigned NumPrevArgs, llvm::SmallVectorImpl<char> &Output, - void *Cookie) { + void *Cookie, + llvm::SmallVectorImpl<intptr_t> &QualTypeVals) { const char *Str = "<can't format argument>"; Output.append(Str, Str+strlen(Str)); } @@ -86,10 +87,12 @@ bool Diagnostic::popMappings(SourceLocation Loc) { void Diagnostic::Reset() { ErrorOccurred = false; FatalErrorOccurred = false; + UnrecoverableErrorOccurred = false; NumWarnings = 0; NumErrors = 0; NumErrorsSuppressed = 0; + CurDiagID = ~0U; // Set LastDiagLevel to an "unset" state. If we set it to 'Ignored', notes // using a Diagnostic associated to a translation unit that follow @@ -542,7 +545,14 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, /// ConvertArgToString, allowing the implementation to avoid redundancies in /// obvious cases. llvm::SmallVector<Diagnostic::ArgumentValue, 8> FormattedArgs; - + + /// QualTypeVals - Pass a vector of arrays so that QualType names can be + /// compared to see if more information is needed to be printed. + llvm::SmallVector<intptr_t, 2> QualTypeVals; + for (unsigned i = 0, e = getNumArgs(); i < e; ++i) + if (getArgKind(i) == Diagnostic::ak_qualtype) + QualTypeVals.push_back(getRawArg(i)); + while (DiagStr != DiagEnd) { if (DiagStr[0] != '%') { // Append non-%0 substrings to Str if we have one. @@ -673,7 +683,7 @@ FormatDiagnostic(const char *DiagStr, const char *DiagEnd, Modifier, ModifierLen, Argument, ArgumentLen, FormattedArgs.data(), FormattedArgs.size(), - OutStr); + OutStr, QualTypeVals); break; } diff --git a/lib/Basic/DiagnosticIDs.cpp b/lib/Basic/DiagnosticIDs.cpp index 6d7e3204cba3..147ba7e99e74 100644 --- a/lib/Basic/DiagnosticIDs.cpp +++ b/lib/Basic/DiagnosticIDs.cpp @@ -14,6 +14,7 @@ #include "clang/AST/ASTDiagnostic.h" #include "clang/Analysis/AnalysisDiagnostic.h" #include "clang/Basic/DiagnosticIDs.h" +#include "clang/Basic/DiagnosticCategories.h" #include "clang/Basic/SourceManager.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Frontend/FrontendDiagnostic.h" @@ -135,7 +136,7 @@ static const unsigned StaticDiagInfoSize = sizeof(StaticDiagInfo)/sizeof(StaticDiagInfo[0])-1; /// To be sorted before first use (since it's splitted among multiple files) -static StaticDiagNameIndexRec StaticDiagNameIndex[] = { +static const StaticDiagNameIndexRec StaticDiagNameIndex[] = { #define DIAG_NAME_INDEX(ENUM) { #ENUM, diag::ENUM, STR_SIZE(#ENUM, uint8_t) }, #include "clang/Basic/DiagnosticIndexName.inc" #undef DIAG_NAME_INDEX @@ -199,19 +200,21 @@ unsigned DiagnosticIDs::getCategoryNumberForDiag(unsigned DiagID) { return 0; } -// The diagnostic category names. -struct StaticDiagCategoryRec { - const char *NameStr; - uint8_t NameLen; +namespace { + // The diagnostic category names. + struct StaticDiagCategoryRec { + const char *NameStr; + uint8_t NameLen; - llvm::StringRef getName() const { - return llvm::StringRef(NameStr, NameLen); - } -}; + llvm::StringRef getName() const { + return llvm::StringRef(NameStr, NameLen); + } + }; +} -static StaticDiagCategoryRec CategoryNameTable[] = { +static const StaticDiagCategoryRec CategoryNameTable[] = { #define GET_CATEGORY_TABLE -#define CATEGORY(X) { X, STR_SIZE(X, uint8_t) }, +#define CATEGORY(X, ENUM) { X, STR_SIZE(X, uint8_t) }, #include "clang/Basic/DiagnosticGroups.inc" #undef GET_CATEGORY_TABLE { 0, 0 } @@ -261,7 +264,7 @@ llvm::StringRef DiagnosticIDs::getName(unsigned DiagID) { /// getIdFromName - Given a diagnostic name, return its ID, or 0 unsigned DiagnosticIDs::getIdFromName(llvm::StringRef Name) { - StaticDiagNameIndexRec *StaticDiagNameIndexEnd = + const StaticDiagNameIndexRec *StaticDiagNameIndexEnd = StaticDiagNameIndex + StaticDiagNameIndexSize; if (Name.empty()) { return diag::DIAG_UPPER_LIMIT; } @@ -543,17 +546,21 @@ DiagnosticIDs::getDiagnosticLevel(unsigned DiagID, unsigned DiagClass, return Result; } -struct WarningOption { - // Be safe with the size of 'NameLen' because we don't statically check if the - // size will fit in the field; the struct size won't decrease with a shorter - // type anyway. - size_t NameLen; - const char *NameStr; - const short *Members; - const short *SubGroups; - - llvm::StringRef getName() const { return llvm::StringRef(NameStr, NameLen); } -}; +namespace { + struct WarningOption { + // Be safe with the size of 'NameLen' because we don't statically check if + // the size will fit in the field; the struct size won't decrease with a + // shorter type anyway. + size_t NameLen; + const char *NameStr; + const short *Members; + const short *SubGroups; + + llvm::StringRef getName() const { + return llvm::StringRef(NameStr, NameLen); + } + }; +} #define GET_DIAG_ARRAYS #include "clang/Basic/DiagnosticGroups.inc" @@ -678,6 +685,12 @@ bool DiagnosticIDs::ProcessDiag(Diagnostic &Diag) const { return false; if (DiagLevel >= DiagnosticIDs::Error) { + Diag.TrapErrorOccurred = true; + if (isUnrecoverable(DiagID)) { + Diag.TrapUnrecoverableErrorOccurred = true; + Diag.UnrecoverableErrorOccurred = true; + } + if (Diag.Client->IncludeInDiagnosticCounts()) { Diag.ErrorOccurred = true; ++Diag.NumErrors; @@ -714,3 +727,25 @@ bool DiagnosticIDs::ProcessDiag(Diagnostic &Diag) const { return true; } + +bool DiagnosticIDs::isUnrecoverable(unsigned DiagID) const { + if (DiagID >= diag::DIAG_UPPER_LIMIT) { + // Custom diagnostics. + return CustomDiagInfo->getLevel(DiagID) >= DiagnosticIDs::Error; + } + + // Only errors may be unrecoverable. + if (getBuiltinDiagClass(DiagID) < CLASS_ERROR) + return false; + + if (DiagID == diag::err_unavailable || + DiagID == diag::err_unavailable_message) + return false; + + // Currently we consider all ARC errors as recoverable. + if (getCategoryNumberForDiag(DiagID) == + diag::DiagCat_Automatic_Reference_Counting_Issue) + return false; + + return true; +} diff --git a/lib/Basic/IdentifierTable.cpp b/lib/Basic/IdentifierTable.cpp index 4711faa1a519..188e2d46f587 100644 --- a/lib/Basic/IdentifierTable.cpp +++ b/lib/Basic/IdentifierTable.cpp @@ -17,6 +17,7 @@ #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" #include <cstdio> @@ -92,7 +93,8 @@ namespace { KEYBORLAND = 0x100, KEYOPENCL = 0x200, KEYC1X = 0x400, - KEYALL = 0x7ff + KEYARC = 0x800, + KEYALL = 0x0fff }; } @@ -120,7 +122,8 @@ static void AddKeyword(llvm::StringRef Keyword, else if (LangOpts.OpenCL && (Flags & KEYOPENCL)) AddResult = 2; else if (!LangOpts.CPlusPlus && (Flags & KEYNOCXX)) AddResult = 2; else if (LangOpts.C1X && (Flags & KEYC1X)) AddResult = 2; - + else if (LangOpts.ObjCAutoRefCount && (Flags & KEYARC)) AddResult = 2; + // Don't add this keyword if disabled in this language. if (AddResult == 0) return; @@ -394,6 +397,8 @@ ObjCMethodFamily Selector::getMethodFamilyImpl(Selector sel) { if (name == "retainCount") return OMF_retainCount; if (name == "self") return OMF_self; } + + if (name == "performSelector") return OMF_performSelector; // The other method families may begin with a prefix of underscores. while (!name.empty() && name.front() == '_') diff --git a/lib/Basic/SourceManager.cpp b/lib/Basic/SourceManager.cpp index 2de8ab7528ab..45922c15527f 100644 --- a/lib/Basic/SourceManager.cpp +++ b/lib/Basic/SourceManager.cpp @@ -169,11 +169,11 @@ const llvm::MemoryBuffer *ContentCache::getBuffer(Diagnostic &Diag, return Buffer.getPointer(); } -unsigned LineTableInfo::getLineTableFilenameID(const char *Ptr, unsigned Len) { +unsigned LineTableInfo::getLineTableFilenameID(llvm::StringRef Name) { // Look up the filename in the string table, returning the pre-existing value // if it exists. llvm::StringMapEntry<unsigned> &Entry = - FilenameIDs.GetOrCreateValue(Ptr, Ptr+Len, ~0U); + FilenameIDs.GetOrCreateValue(Name, ~0U); if (Entry.getValue() != ~0U) return Entry.getValue(); @@ -277,10 +277,10 @@ void LineTableInfo::AddEntry(unsigned FID, /// getLineTableFilenameID - Return the uniqued ID for the specified filename. /// -unsigned SourceManager::getLineTableFilenameID(const char *Ptr, unsigned Len) { +unsigned SourceManager::getLineTableFilenameID(llvm::StringRef Name) { if (LineTable == 0) LineTable = new LineTableInfo(); - return LineTable->getLineTableFilenameID(Ptr, Len); + return LineTable->getLineTableFilenameID(Name); } @@ -531,16 +531,31 @@ FileID SourceManager::createFileID(const ContentCache *File, return LastFileIDLookup = FID; } -/// createInstantiationLoc - Return a new SourceLocation that encodes the fact -/// that a token from SpellingLoc should actually be referenced from -/// InstantiationLoc. +SourceLocation +SourceManager::createMacroArgInstantiationLoc(SourceLocation SpellingLoc, + SourceLocation ILoc, + unsigned TokLength) { + InstantiationInfo II = + InstantiationInfo::createForMacroArg(SpellingLoc, ILoc); + return createInstantiationLocImpl(II, TokLength); +} + SourceLocation SourceManager::createInstantiationLoc(SourceLocation SpellingLoc, SourceLocation ILocStart, SourceLocation ILocEnd, unsigned TokLength, unsigned PreallocatedID, unsigned Offset) { - InstantiationInfo II = InstantiationInfo::get(ILocStart,ILocEnd, SpellingLoc); + InstantiationInfo II = + InstantiationInfo::create(SpellingLoc, ILocStart, ILocEnd); + return createInstantiationLocImpl(II, TokLength, PreallocatedID, Offset); +} + +SourceLocation +SourceManager::createInstantiationLocImpl(const InstantiationInfo &II, + unsigned TokLength, + unsigned PreallocatedID, + unsigned Offset) { if (PreallocatedID) { // If we're filling in a preallocated ID, just load in the // instantiation entry and return. @@ -749,18 +764,19 @@ SourceLocation SourceManager::getSpellingLocSlowCase(SourceLocation Loc) const { std::pair<FileID, unsigned> -SourceManager::getDecomposedInstantiationLocSlowCase(const SrcMgr::SLocEntry *E, - unsigned Offset) const { +SourceManager::getDecomposedInstantiationLocSlowCase( + const SrcMgr::SLocEntry *E) const { // If this is an instantiation record, walk through all the instantiation // points. FileID FID; SourceLocation Loc; + unsigned Offset; do { Loc = E->getInstantiation().getInstantiationLocStart(); FID = getFileID(Loc); E = &getSLocEntry(FID); - Offset += Loc.getOffset()-E->getOffset(); + Offset = Loc.getOffset()-E->getOffset(); } while (!Loc.isFileID()); return std::make_pair(FID, Offset); @@ -823,6 +839,14 @@ SourceManager::getInstantiationRange(SourceLocation Loc) const { return Res; } +bool SourceManager::isMacroArgInstantiation(SourceLocation Loc) const { + if (!Loc.isMacroID()) return false; + + FileID FID = getFileID(Loc); + const SrcMgr::SLocEntry *E = &getSLocEntry(FID); + const SrcMgr::InstantiationInfo &II = E->getInstantiation(); + return II.isMacroArgInstantiation(); +} //===----------------------------------------------------------------------===// @@ -917,7 +941,7 @@ static void ComputeLineNumbers(Diagnostic &Diag, ContentCache *FI, // Find the file offsets of all of the *physical* source lines. This does // not look at trigraphs, escaped newlines, or anything else tricky. - std::vector<unsigned> LineOffsets; + llvm::SmallVector<unsigned, 256> LineOffsets; // Line #1 starts at char 0. LineOffsets.push_back(0); @@ -1213,73 +1237,6 @@ PresumedLoc SourceManager::getPresumedLoc(SourceLocation Loc) const { return PresumedLoc(Filename, LineNo, ColNo, IncludeLoc); } -/// \brief Returns true if the given MacroID location points at the first -/// token of the macro instantiation. -bool SourceManager::isAtStartOfMacroInstantiation(SourceLocation loc) const { - assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc"); - - unsigned FID = getFileID(loc).ID; - assert(FID > 1); - std::pair<SourceLocation, SourceLocation> - instRange = getImmediateInstantiationRange(loc); - - bool invalid = false; - const SrcMgr::SLocEntry &Entry = getSLocEntry(FID-1, &invalid); - if (invalid) - return false; - - // If the FileID immediately before it is a file then this is the first token - // in the macro. - if (Entry.isFile()) - return true; - - // If the FileID immediately before it (which is a macro token) is the - // immediate instantiated macro, check this macro token's location. - if (getFileID(instRange.second).ID == FID-1) - return isAtStartOfMacroInstantiation(instRange.first); - - // If the FileID immediately before it (which is a macro token) came from a - // different instantiation, then this is the first token in the macro. - if (getInstantiationLoc(Entry.getInstantiation().getInstantiationLocStart()) - != getInstantiationLoc(loc)) - return true; - - // It is inside the macro or the last token in the macro. - return false; -} - -/// \brief Returns true if the given MacroID location points at the last -/// token of the macro instantiation. -bool SourceManager::isAtEndOfMacroInstantiation(SourceLocation loc) const { - assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc"); - - unsigned FID = getFileID(loc).ID; - assert(FID > 1); - std::pair<SourceLocation, SourceLocation> - instRange = getInstantiationRange(loc); - - // If there's no FileID after it, it is the last token in the macro. - if (FID+1 == sloc_entry_size()) - return true; - - bool invalid = false; - const SrcMgr::SLocEntry &Entry = getSLocEntry(FID+1, &invalid); - if (invalid) - return false; - - // If the FileID immediately after it is a file or a macro token which - // came from a different instantiation, then this is the last token in the - // macro. - if (Entry.isFile()) - return true; - if (getInstantiationLoc(Entry.getInstantiation().getInstantiationLocStart()) - != instRange.first) - return true; - - // It is inside the macro or the first token in the macro. - return false; -} - //===----------------------------------------------------------------------===// // Other miscellaneous methods. //===----------------------------------------------------------------------===// @@ -1474,8 +1431,6 @@ bool SourceManager::isBeforeInTranslationUnit(SourceLocation LHS, // reflect the order that the tokens, pointed to by these locations, were // instantiated (during parsing each token that is instantiated by a macro, // expands the SLocEntries). - if (LHS.isMacroID() && RHS.isMacroID()) - return LHS.getOffset() < RHS.getOffset(); std::pair<FileID, unsigned> LOffs = getDecomposedLoc(LHS); std::pair<FileID, unsigned> ROffs = getDecomposedLoc(RHS); @@ -1562,7 +1517,9 @@ void SourceManager::PrintStats() const { llvm::errs() << "\n*** Source Manager Stats:\n"; llvm::errs() << FileInfos.size() << " files mapped, " << MemBufferInfos.size() << " mem buffers mapped.\n"; - llvm::errs() << SLocEntryTable.size() << " SLocEntry's allocated, " + llvm::errs() << SLocEntryTable.size() << " SLocEntry's allocated (" + << SLocEntryTable.capacity()*sizeof(SrcMgr::SLocEntry) + << " bytes of capacity), " << NextOffset << "B of Sloc address space used.\n"; unsigned NumLineNumsComputed = 0; diff --git a/lib/Basic/TargetInfo.cpp b/lib/Basic/TargetInfo.cpp index dcf0cb4237a9..30a9bdb31774 100644 --- a/lib/Basic/TargetInfo.cpp +++ b/lib/Basic/TargetInfo.cpp @@ -181,6 +181,14 @@ static llvm::StringRef removeGCCRegisterPrefix(llvm::StringRef Name) { return Name; } +/// isValidClobber - Returns whether the passed in string is +/// a valid clobber in an inline asm statement. This is used by +/// Sema. +bool TargetInfo::isValidClobber(llvm::StringRef Name) const { + return (isValidGCCRegisterName(Name) || + Name == "memory" || Name == "cc"); +} + /// isValidGCCRegisterName - Returns whether the passed in string /// is a valid register name according to GCC. This is used by Sema for /// inline asm statements. @@ -194,9 +202,6 @@ bool TargetInfo::isValidGCCRegisterName(llvm::StringRef Name) const { // Get rid of any register prefix. Name = removeGCCRegisterPrefix(Name); - if (Name == "memory" || Name == "cc") - return true; - getGCCRegNames(Names, NumNames); // If we have a number it maps to an entry in the register name array. @@ -212,6 +217,20 @@ bool TargetInfo::isValidGCCRegisterName(llvm::StringRef Name) const { return true; } + // Check any additional names that we have. + const AddlRegName *AddlNames; + unsigned NumAddlNames; + getGCCAddlRegNames(AddlNames, NumAddlNames); + for (unsigned i = 0; i < NumAddlNames; i++) + for (unsigned j = 0; j < llvm::array_lengthof(AddlNames[i].Names); j++) { + if (!AddlNames[i].Names[j]) + break; + // Make sure the register that the additional name is for is within + // the bounds of the register names from above. + if (AddlNames[i].Names[j] == Name && AddlNames[i].RegNum < NumNames) + return true; + } + // Now check aliases. const GCCRegAlias *Aliases; unsigned NumAliases; @@ -251,6 +270,20 @@ TargetInfo::getNormalizedGCCRegisterName(llvm::StringRef Name) const { } } + // Check any additional names that we have. + const AddlRegName *AddlNames; + unsigned NumAddlNames; + getGCCAddlRegNames(AddlNames, NumAddlNames); + for (unsigned i = 0; i < NumAddlNames; i++) + for (unsigned j = 0; j < llvm::array_lengthof(AddlNames[i].Names); j++) { + if (!AddlNames[i].Names[j]) + break; + // Make sure the register that the additional name is for is within + // the bounds of the register names from above. + if (AddlNames[i].Names[j] == Name && AddlNames[i].RegNum < NumNames) + return Name; + } + // Now check aliases. const GCCRegAlias *Aliases; unsigned NumAliases; diff --git a/lib/Basic/Targets.cpp b/lib/Basic/Targets.cpp index dd167dca47b2..3518ea6f7986 100644 --- a/lib/Basic/Targets.cpp +++ b/lib/Basic/Targets.cpp @@ -84,14 +84,28 @@ static void getDarwinDefines(MacroBuilder &Builder, const LangOptions &Opts, Builder.defineMacro("__MACH__"); Builder.defineMacro("OBJC_NEW_PROPERTIES"); - // __weak is always defined, for use in blocks and with objc pointers. - Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); + if (!Opts.ObjCAutoRefCount) { + // __weak is always defined, for use in blocks and with objc pointers. + Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))"); - // Darwin defines __strong even in C mode (just to nothing). - if (!Opts.ObjC1 || Opts.getGCMode() == LangOptions::NonGC) - Builder.defineMacro("__strong", ""); - else - Builder.defineMacro("__strong", "__attribute__((objc_gc(strong)))"); + // Darwin defines __strong even in C mode (just to nothing). + if (Opts.getGCMode() != LangOptions::NonGC) + Builder.defineMacro("__strong", "__attribute__((objc_gc(strong)))"); + else + Builder.defineMacro("__strong", ""); + + // __unsafe_unretained is defined to nothing in non-ARC mode. We even + // allow this in C, since one might have block pointers in structs that + // are used in pure C code and in Objective-C ARC. + Builder.defineMacro("__unsafe_unretained", ""); + + // The Objective-C bridged cast keywords are defined to nothing in non-ARC + // mode; then they become normal, C-style casts. + Builder.defineMacro("__bridge", ""); + Builder.defineMacro("__bridge_transfer", ""); + Builder.defineMacro("__bridge_retained", ""); + Builder.defineMacro("__bridge_retain", ""); + } if (Opts.Static) Builder.defineMacro("__STATIC__"); @@ -168,14 +182,15 @@ class DarwinTargetInfo : public OSTargetInfo<Target> { protected: virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, MacroBuilder &Builder) const { - getDarwinDefines(Builder, Opts, Triple, this->PlatformName, + getDarwinDefines(Builder, Opts, Triple, this->PlatformName, this->PlatformMinVersion); } public: DarwinTargetInfo(const std::string& triple) : OSTargetInfo<Target>(triple) { - this->TLSSupported = llvm::Triple(triple).getDarwinMajorNumber() > 10; + llvm::Triple T = llvm::Triple(triple); + this->TLSSupported = T.isMacOSX() && !T.isMacOSXVersionLT(10,7); this->MCountName = "\01mcount"; } @@ -512,7 +527,6 @@ class PPCTargetInfo : public TargetInfo { static const Builtin::Info BuiltinInfo[]; static const char * const GCCRegNames[]; static const TargetInfo::GCCRegAlias GCCRegAliases[]; - public: PPCTargetInfo(const std::string& triple) : TargetInfo(triple) {} @@ -633,9 +647,9 @@ public: }; const Builtin::Info PPCTargetInfo::BuiltinInfo[] = { -#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES, false }, +#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\ - ALL_LANGUAGES, false }, + ALL_LANGUAGES }, #include "clang/Basic/BuiltinsPPC.def" }; @@ -660,7 +674,8 @@ void PPCTargetInfo::getTargetDefines(const LangOptions &Opts, } // Target properties. - Builder.defineMacro("_BIG_ENDIAN"); + if (getTriple().getOS() != llvm::Triple::NetBSD) + Builder.defineMacro("_BIG_ENDIAN"); Builder.defineMacro("__BIG_ENDIAN__"); // Subtarget options. @@ -788,8 +803,14 @@ public: DescriptionString = "E-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" "i64:64:64-f32:32:32-f64:64:64-v128:128:128-n32"; - if (getTriple().getOS() == llvm::Triple::FreeBSD) - SizeType = UnsignedInt; + switch (getTriple().getOS()) { + case llvm::Triple::FreeBSD: + case llvm::Triple::NetBSD: + SizeType = UnsignedInt; + break; + default: + break; + } } virtual const char *getVAListDeclaration() const { @@ -890,9 +911,9 @@ namespace { }; const Builtin::Info PTXTargetInfo::BuiltinInfo[] = { -#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES, false }, +#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\ - ALL_LANGUAGES, false }, + ALL_LANGUAGES }, #include "clang/Basic/BuiltinsPTX.def" }; @@ -1058,49 +1079,49 @@ void MBlazeTargetInfo::getGCCRegAliases(const GCCRegAlias *&Aliases, namespace { // Namespace for x86 abstract base class const Builtin::Info BuiltinInfo[] = { -#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES, false }, +#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\ - ALL_LANGUAGES, false }, + ALL_LANGUAGES }, #include "clang/Basic/BuiltinsX86.def" }; static const char* const GCCRegNames[] = { "ax", "dx", "cx", "bx", "si", "di", "bp", "sp", "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)", - "argp", "flags", "fspr", "dirflag", "frame", + "argp", "flags", "fpcr", "fpsr", "dirflag", "frame", "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15" + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", }; -const TargetInfo::GCCRegAlias GCCRegAliases[] = { - { { "al", "ah", "eax", "rax" }, "ax" }, - { { "bl", "bh", "ebx", "rbx" }, "bx" }, - { { "cl", "ch", "ecx", "rcx" }, "cx" }, - { { "dl", "dh", "edx", "rdx" }, "dx" }, - { { "esi", "rsi" }, "si" }, - { { "edi", "rdi" }, "di" }, - { { "esp", "rsp" }, "sp" }, - { { "ebp", "rbp" }, "bp" }, +const TargetInfo::AddlRegName AddlRegNames[] = { + { { "al", "ah", "eax", "rax" }, 0 }, + { { "bl", "bh", "ebx", "rbx" }, 3 }, + { { "cl", "ch", "ecx", "rcx" }, 2 }, + { { "dl", "dh", "edx", "rdx" }, 1 }, + { { "esi", "rsi" }, 4 }, + { { "edi", "rdi" }, 5 }, + { { "esp", "rsp" }, 7 }, + { { "ebp", "rbp" }, 6 }, }; // X86 target abstract base class; x86-32 and x86-64 are very close, so // most of the implementation can be shared. class X86TargetInfo : public TargetInfo { enum X86SSEEnum { - NoMMXSSE, MMX, SSE1, SSE2, SSE3, SSSE3, SSE41, SSE42 + NoSSE, SSE1, SSE2, SSE3, SSSE3, SSE41, SSE42 } SSELevel; - enum AMD3DNowEnum { - NoAMD3DNow, AMD3DNow, AMD3DNowAthlon - } AMD3DNowLevel; + enum MMX3DNowEnum { + NoMMX3DNow, MMX, AMD3DNow, AMD3DNowAthlon + } MMX3DNowLevel; bool HasAES; bool HasAVX; public: X86TargetInfo(const std::string& triple) - : TargetInfo(triple), SSELevel(NoMMXSSE), AMD3DNowLevel(NoAMD3DNow), + : TargetInfo(triple), SSELevel(NoSSE), MMX3DNowLevel(NoMMX3DNow), HasAES(false), HasAVX(false) { LongDoubleFormat = &llvm::APFloat::x87DoubleExtended; } @@ -1116,8 +1137,13 @@ public: } virtual void getGCCRegAliases(const GCCRegAlias *&Aliases, unsigned &NumAliases) const { - Aliases = GCCRegAliases; - NumAliases = llvm::array_lengthof(GCCRegAliases); + Aliases = 0; + NumAliases = 0; + } + virtual void getGCCAddlRegNames(const AddlRegName *&Names, + unsigned &NumNames) const { + Names = AddlRegNames; + NumNames = llvm::array_lengthof(AddlRegNames); } virtual bool validateAsmConstraint(const char *&Name, TargetInfo::ConstraintInfo &info) const; @@ -1133,6 +1159,9 @@ public: virtual void getDefaultFeatures(const std::string &CPU, llvm::StringMap<bool> &Features) const; virtual void HandleTargetFeatures(std::vector<std::string> &Features); + virtual const char* getABI() const { + return MMX3DNowLevel == NoMMX3DNow ? "no-mmx" : ""; + } }; void X86TargetInfo::getDefaultFeatures(const std::string &CPU, @@ -1164,31 +1193,38 @@ void X86TargetInfo::getDefaultFeatures(const std::string &CPU, ; else if (CPU == "pentium-mmx" || CPU == "pentium2") setFeatureEnabled(Features, "mmx", true); - else if (CPU == "pentium3") + else if (CPU == "pentium3") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse", true); - else if (CPU == "pentium-m" || CPU == "pentium4" || CPU == "x86-64") + } else if (CPU == "pentium-m" || CPU == "pentium4" || CPU == "x86-64") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse2", true); - else if (CPU == "yonah" || CPU == "prescott" || CPU == "nocona") + } else if (CPU == "yonah" || CPU == "prescott" || CPU == "nocona") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse3", true); - else if (CPU == "core2") + } else if (CPU == "core2") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "ssse3", true); - else if (CPU == "penryn") { + } else if (CPU == "penryn") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse4", true); Features["sse42"] = false; - } else if (CPU == "atom") + } else if (CPU == "atom") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse3", true); - else if (CPU == "corei7") { + } else if (CPU == "corei7") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse4", true); setFeatureEnabled(Features, "aes", true); } else if (CPU == "corei7-avx") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse4", true); setFeatureEnabled(Features, "aes", true); -// setFeatureEnabled(Features, "avx", true); + //setFeatureEnabled(Features, "avx", true); } else if (CPU == "k6" || CPU == "winchip-c6") setFeatureEnabled(Features, "mmx", true); else if (CPU == "k6-2" || CPU == "k6-3" || CPU == "athlon" || CPU == "athlon-tbird" || CPU == "winchip2" || CPU == "c3") { - setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "3dnow", true); } else if (CPU == "athlon-4" || CPU == "athlon-xp" || CPU == "athlon-mp") { setFeatureEnabled(Features, "sse", true); @@ -1200,8 +1236,10 @@ void X86TargetInfo::getDefaultFeatures(const std::string &CPU, } else if (CPU == "k8-sse3") { setFeatureEnabled(Features, "sse3", true); setFeatureEnabled(Features, "3dnowa", true); - } else if (CPU == "c3-2") + } else if (CPU == "c3-2") { + setFeatureEnabled(Features, "mmx", true); setFeatureEnabled(Features, "sse", true); + } } bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, @@ -1217,34 +1255,31 @@ bool X86TargetInfo::setFeatureEnabled(llvm::StringMap<bool> &Features, if (Name == "mmx") Features["mmx"] = true; else if (Name == "sse") - Features["mmx"] = Features["sse"] = true; + Features["sse"] = true; else if (Name == "sse2") - Features["mmx"] = Features["sse"] = Features["sse2"] = true; + Features["sse"] = Features["sse2"] = true; else if (Name == "sse3") - Features["mmx"] = Features["sse"] = Features["sse2"] = - Features["sse3"] = true; + Features["sse"] = Features["sse2"] = Features["sse3"] = true; else if (Name == "ssse3") - Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = + Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = true; else if (Name == "sse4" || Name == "sse4.2") - Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = + Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = Features["sse42"] = true; else if (Name == "sse4.1") - Features["mmx"] = Features["sse"] = Features["sse2"] = Features["sse3"] = + Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = true; else if (Name == "3dnow") - Features["3dnowa"] = true; + Features["mmx"] = Features["3dnow"] = true; else if (Name == "3dnowa") - Features["3dnow"] = Features["3dnowa"] = true; + Features["mmx"] = Features["3dnow"] = Features["3dnowa"] = true; else if (Name == "aes") Features["aes"] = true; else if (Name == "avx") Features["avx"] = true; } else { if (Name == "mmx") - Features["mmx"] = Features["3dnow"] = Features["3dnowa"] = - Features["sse"] = Features["sse2"] = Features["sse3"] = - Features["ssse3"] = Features["sse41"] = Features["sse42"] = false; + Features["mmx"] = Features["3dnow"] = Features["3dnowa"] = false; else if (Name == "sse") Features["sse"] = Features["sse2"] = Features["sse3"] = Features["ssse3"] = Features["sse41"] = Features["sse42"] = false; @@ -1302,18 +1337,25 @@ void X86TargetInfo::HandleTargetFeatures(std::vector<std::string> &Features) { .Case("sse3", SSE3) .Case("sse2", SSE2) .Case("sse", SSE1) - .Case("mmx", MMX) - .Default(NoMMXSSE); + .Default(NoSSE); SSELevel = std::max(SSELevel, Level); - AMD3DNowEnum ThreeDNowLevel = - llvm::StringSwitch<AMD3DNowEnum>(Features[i].substr(1)) + MMX3DNowEnum ThreeDNowLevel = + llvm::StringSwitch<MMX3DNowEnum>(Features[i].substr(1)) .Case("3dnowa", AMD3DNowAthlon) .Case("3dnow", AMD3DNow) - .Default(NoAMD3DNow); + .Case("mmx", MMX) + .Default(NoMMX3DNow); - AMD3DNowLevel = std::max(AMD3DNowLevel, ThreeDNowLevel); + MMX3DNowLevel = std::max(MMX3DNowLevel, ThreeDNowLevel); } + + // Don't tell the backend if we're turning off mmx; it will end up disabling + // SSE, which we don't want. + std::vector<std::string>::iterator it; + it = std::find(Features.begin(), Features.end(), "-mmx"); + if (it != Features.end()) + Features.erase(it); } /// X86TargetInfo::getTargetDefines - Return a set of the X86-specific #defines @@ -1368,9 +1410,7 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, case SSE1: Builder.defineMacro("__SSE__"); Builder.defineMacro("__SSE_MATH__"); // -mfp-math=sse always implied. - case MMX: - Builder.defineMacro("__MMX__"); - case NoMMXSSE: + case NoSSE: break; } @@ -1392,12 +1432,14 @@ void X86TargetInfo::getTargetDefines(const LangOptions &Opts, } // Each case falls through to the previous one here. - switch (AMD3DNowLevel) { + switch (MMX3DNowLevel) { case AMD3DNowAthlon: Builder.defineMacro("__3dNOW_A__"); case AMD3DNow: Builder.defineMacro("__3dNOW__"); - case NoAMD3DNow: + case MMX: + Builder.defineMacro("__MMX__"); + case NoMMX3DNow: break; } } @@ -1647,6 +1689,65 @@ public: }; } // end anonymous namespace +// RTEMS Target +template<typename Target> +class RTEMSTargetInfo : public OSTargetInfo<Target> { +protected: + virtual void getOSDefines(const LangOptions &Opts, const llvm::Triple &Triple, + MacroBuilder &Builder) const { + // RTEMS defines; list based off of gcc output + + // FIXME: Move version number handling to llvm::Triple. + llvm::StringRef Release = Triple.getOSName().substr(strlen("rtems"), 1); + + Builder.defineMacro("__rtems__"); + Builder.defineMacro("__ELF__"); + } +public: + RTEMSTargetInfo(const std::string &triple) + : OSTargetInfo<Target>(triple) { + this->UserLabelPrefix = ""; + + llvm::Triple Triple(triple); + switch (Triple.getArch()) { + default: + case llvm::Triple::x86: + // this->MCountName = ".mcount"; + break; + case llvm::Triple::mips: + case llvm::Triple::mipsel: + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + // this->MCountName = "_mcount"; + break; + case llvm::Triple::arm: + // this->MCountName = "__mcount"; + break; + } + + } +}; + +namespace { +// x86-32 RTEMS target +class RTEMSX86_32TargetInfo : public X86_32TargetInfo { +public: + RTEMSX86_32TargetInfo(const std::string& triple) + : X86_32TargetInfo(triple) { + SizeType = UnsignedLong; + IntPtrType = SignedLong; + PtrDiffType = SignedLong; + this->UserLabelPrefix = ""; + } + virtual void getTargetDefines(const LangOptions &Opts, + MacroBuilder &Builder) const { + X86_32TargetInfo::getTargetDefines(Opts, Builder); + Builder.defineMacro("__INTEL__"); + Builder.defineMacro("__rtems__"); + } +}; +} // end anonymous namespace + namespace { // x86-64 generic target class X86_64TargetInfo : public X86TargetInfo { @@ -1861,7 +1962,7 @@ public: // Thumb1 add sp, #imm requires the immediate value be multiple of 4, // so set preferred for small types to 32. DescriptionString = ("e-p:32:32:32-i1:8:32-i8:8:32-i16:16:32-i32:32:32-" - "i64:32:32-f32:32:32-f64:32:32-" + "i64:32:64-f32:32:32-f64:32:64-" "v64:32:64-v128:32:128-a0:0:32-n32"); } else { DescriptionString = ("e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-" @@ -1882,11 +1983,6 @@ public: void getDefaultFeatures(const std::string &CPU, llvm::StringMap<bool> &Features) const { - // FIXME: This should not be here. - Features["vfp2"] = false; - Features["vfp3"] = false; - Features["neon"] = false; - if (CPU == "arm1136jf-s" || CPU == "arm1176jzf-s" || CPU == "mpcore") Features["vfp2"] = true; else if (CPU == "cortex-a8" || CPU == "cortex-a9") @@ -1896,12 +1992,8 @@ public: virtual bool setFeatureEnabled(llvm::StringMap<bool> &Features, const std::string &Name, bool Enabled) const { - if (Name == "soft-float" || Name == "soft-float-abi") { - Features[Name] = Enabled; - } else if (Name == "vfp2" || Name == "vfp3" || Name == "neon") { - // These effectively are a single option, reset them when any is enabled. - if (Enabled) - Features["vfp2"] = Features["vfp3"] = Features["neon"] = false; + if (Name == "soft-float" || Name == "soft-float-abi" || + Name == "vfp2" || Name == "vfp3" || Name == "neon") { Features[Name] = Enabled; } else return false; @@ -2042,6 +2134,12 @@ public: case 'q': // ...ARMV4 ldrsb case 'v': // ...VFP load/store (reg+constant offset) case 'y': // ...iWMMXt load/store + case 't': // address valid for load/store opaque types wider + // than 128-bits + case 'n': // valid address for Neon doubleword vector load/store + case 'm': // valid address for Neon element and structure load/store + case 's': // valid address for non-offset loads/stores of quad-word + // values in four ARM registers Info.setAllowsMemory(); Name++; return true; @@ -2049,14 +2147,16 @@ public: } return false; } - std::string - virtual convertConstraint(const char *&Constraint) const { + virtual std::string convertConstraint(const char *&Constraint) const { std::string R; switch (*Constraint) { case 'U': // Two-character constraint; add "^" hint for later parsing. R = std::string("^") + std::string(Constraint, 2); Constraint++; break; + case 'p': // 'p' should be translated to 'r' by default. + R = std::string("r"); + break; default: return std::string(1, *Constraint); } @@ -2124,9 +2224,9 @@ void ARMTargetInfo::getGCCRegAliases(const GCCRegAlias *&Aliases, } const Builtin::Info ARMTargetInfo::BuiltinInfo[] = { -#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES, false }, +#define BUILTIN(ID, TYPE, ATTRS) { #ID, TYPE, ATTRS, 0, ALL_LANGUAGES }, #define LIBBUILTIN(ID, TYPE, ATTRS, HEADER) { #ID, TYPE, ATTRS, HEADER,\ - ALL_LANGUAGES, false }, + ALL_LANGUAGES }, #include "clang/Basic/BuiltinsARM.def" }; } // end anonymous namespace. @@ -2727,45 +2827,81 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new LinuxTargetInfo<ARMTargetInfo>(T); case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo<ARMTargetInfo>(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo<ARMTargetInfo>(T); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo<ARMTargetInfo>(T); default: return new ARMTargetInfo(T); } case llvm::Triple::bfin: + if ( os == llvm::Triple::RTEMS ) + return new RTEMSTargetInfo<BlackfinTargetInfo>(T); return new BlackfinTargetInfo(T); case llvm::Triple::msp430: return new MSP430TargetInfo(T); case llvm::Triple::mips: - if (os == llvm::Triple::Psp) + switch (os) { + case llvm::Triple::Psp: return new PSPTargetInfo<MipsTargetInfo>(T); - if (os == llvm::Triple::Linux) + case llvm::Triple::Linux: return new LinuxTargetInfo<MipsTargetInfo>(T); - return new MipsTargetInfo(T); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo<MipsTargetInfo>(T); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo<MipsTargetInfo>(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo<MipsTargetInfo>(T); + default: + return new MipsTargetInfo(T); + } case llvm::Triple::mipsel: - if (os == llvm::Triple::Psp) + switch (os) { + case llvm::Triple::Psp: return new PSPTargetInfo<MipselTargetInfo>(T); - if (os == llvm::Triple::Linux) + case llvm::Triple::Linux: return new LinuxTargetInfo<MipselTargetInfo>(T); - return new MipselTargetInfo(T); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo<MipselTargetInfo>(T); + case llvm::Triple::FreeBSD: + return new FreeBSDTargetInfo<MipselTargetInfo>(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo<MipselTargetInfo>(T); + default: + return new MipsTargetInfo(T); + } case llvm::Triple::ppc: if (Triple.isOSDarwin()) return new DarwinPPC32TargetInfo(T); - else if (os == llvm::Triple::FreeBSD) + switch (os) { + case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo<PPC32TargetInfo>(T); - return new PPC32TargetInfo(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo<PPC32TargetInfo>(T); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo<PPC32TargetInfo>(T); + default: + return new PPC32TargetInfo(T); + } case llvm::Triple::ppc64: if (Triple.isOSDarwin()) return new DarwinPPC64TargetInfo(T); - else if (os == llvm::Triple::Lv2) + switch (os) { + case llvm::Triple::Lv2: return new PS3PPUTargetInfo<PPC64TargetInfo>(T); - else if (os == llvm::Triple::FreeBSD) + case llvm::Triple::FreeBSD: return new FreeBSDTargetInfo<PPC64TargetInfo>(T); - return new PPC64TargetInfo(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo<PPC64TargetInfo>(T); + default: + return new PPC64TargetInfo(T); + } case llvm::Triple::ptx32: return new PTX32TargetInfo(T); @@ -2776,11 +2912,18 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new MBlazeTargetInfo(T); case llvm::Triple::sparc: - if (os == llvm::Triple::AuroraUX) + switch (os) { + case llvm::Triple::AuroraUX: return new AuroraUXSparcV8TargetInfo(T); - if (os == llvm::Triple::Solaris) + case llvm::Triple::Solaris: return new SolarisSparcV8TargetInfo(T); - return new SparcV8TargetInfo(T); + case llvm::Triple::NetBSD: + return new NetBSDTargetInfo<SparcV8TargetInfo>(T); + case llvm::Triple::RTEMS: + return new RTEMSTargetInfo<SparcV8TargetInfo>(T); + default: + return new SparcV8TargetInfo(T); + } // FIXME: Need a real SPU target. case llvm::Triple::cellspu: @@ -2821,6 +2964,8 @@ static TargetInfo *AllocateTarget(const std::string &T) { return new VisualStudioWindowsX86_32TargetInfo(T); case llvm::Triple::Haiku: return new HaikuX86_32TargetInfo(T); + case llvm::Triple::RTEMS: + return new RTEMSX86_32TargetInfo(T); default: return new X86_32TargetInfo(T); } @@ -2910,7 +3055,8 @@ TargetInfo *TargetInfo::CreateTargetInfo(Diagnostic &Diags, Opts.Features.clear(); for (llvm::StringMap<bool>::const_iterator it = Features.begin(), ie = Features.end(); it != ie; ++it) - Opts.Features.push_back(std::string(it->second ? "+" : "-") + it->first()); + Opts.Features.push_back(std::string(it->second ? "+" : "-") + + it->first().str()); Target->HandleTargetFeatures(Opts.Features); return Target.take(); diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b4574344bc5f..7b46c6040a9c 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(Sema) add_subdirectory(CodeGen) add_subdirectory(Analysis) add_subdirectory(Rewrite) +add_subdirectory(ARCMigrate) add_subdirectory(Driver) add_subdirectory(Serialization) add_subdirectory(Frontend) diff --git a/lib/CodeGen/ABIInfo.h b/lib/CodeGen/ABIInfo.h index ce1039849b7c..138123816c3e 100644 --- a/lib/CodeGen/ABIInfo.h +++ b/lib/CodeGen/ABIInfo.h @@ -68,22 +68,22 @@ namespace clang { private: Kind TheKind; - llvm::PATypeHolder TypeData; + llvm::Type *TypeData; unsigned UIntData; bool BoolData0; bool BoolData1; - ABIArgInfo(Kind K, const llvm::Type *TD=0, + ABIArgInfo(Kind K, llvm::Type *TD=0, unsigned UI=0, bool B0 = false, bool B1 = false) : TheKind(K), TypeData(TD), UIntData(UI), BoolData0(B0), BoolData1(B1) {} public: ABIArgInfo() : TheKind(Direct), TypeData(0), UIntData(0) {} - static ABIArgInfo getDirect(const llvm::Type *T = 0, unsigned Offset = 0) { + static ABIArgInfo getDirect(llvm::Type *T = 0, unsigned Offset = 0) { return ABIArgInfo(Direct, T, Offset); } - static ABIArgInfo getExtend(const llvm::Type *T = 0) { + static ABIArgInfo getExtend(llvm::Type *T = 0) { return ABIArgInfo(Extend, T, 0); } static ABIArgInfo getIgnore() { @@ -113,12 +113,12 @@ namespace clang { assert((isDirect() || isExtend()) && "Not a direct or extend kind"); return UIntData; } - const llvm::Type *getCoerceToType() const { + llvm::Type *getCoerceToType() const { assert(canHaveCoerceToType() && "Invalid kind!"); return TypeData; } - void setCoerceToType(const llvm::Type *T) { + void setCoerceToType(llvm::Type *T) { assert(canHaveCoerceToType() && "Invalid kind!"); TypeData = T; } diff --git a/lib/CodeGen/BackendUtil.cpp b/lib/CodeGen/BackendUtil.cpp index 01d15ff7f7fd..85f42db81f59 100644 --- a/lib/CodeGen/BackendUtil.cpp +++ b/lib/CodeGen/BackendUtil.cpp @@ -10,6 +10,7 @@ #include "clang/CodeGen/BackendUtil.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetOptions.h" +#include "clang/Basic/LangOptions.h" #include "clang/Frontend/CodeGenOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "llvm/Module.h" @@ -18,13 +19,13 @@ #include "llvm/Bitcode/ReaderWriter.h" #include "llvm/CodeGen/RegAllocRegistry.h" #include "llvm/CodeGen/SchedulerRegistry.h" +#include "llvm/MC/SubtargetFeature.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/PassManagerBuilder.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Target/SubtargetFeature.h" #include "llvm/Target/TargetData.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" @@ -39,6 +40,7 @@ class EmitAssemblyHelper { Diagnostic &Diags; const CodeGenOptions &CodeGenOpts; const TargetOptions &TargetOpts; + const LangOptions &LangOpts; Module *TheModule; Timer CodeGenerationTime; @@ -82,8 +84,9 @@ private: public: EmitAssemblyHelper(Diagnostic &_Diags, const CodeGenOptions &CGOpts, const TargetOptions &TOpts, + const LangOptions &LOpts, Module *M) - : Diags(_Diags), CodeGenOpts(CGOpts), TargetOpts(TOpts), + : Diags(_Diags), CodeGenOpts(CGOpts), TargetOpts(TOpts), LangOpts(LOpts), TheModule(M), CodeGenerationTime("Code Generation Time"), CodeGenPasses(0), PerModulePasses(0), PerFunctionPasses(0) {} @@ -98,6 +101,16 @@ public: } +static void addObjCARCExpandPass(const PassManagerBuilder &Builder, PassManagerBase &PM) { + if (Builder.OptLevel > 0) + PM.add(createObjCARCExpandPass()); +} + +static void addObjCARCOptPass(const PassManagerBuilder &Builder, PassManagerBase &PM) { + if (Builder.OptLevel > 0) + PM.add(createObjCARCOptPass()); +} + void EmitAssemblyHelper::CreatePasses() { unsigned OptLevel = CodeGenOpts.OptimizationLevel; CodeGenOptions::InliningMethod Inlining = CodeGenOpts.Inlining; @@ -116,6 +129,14 @@ void EmitAssemblyHelper::CreatePasses() { PMBuilder.DisableSimplifyLibCalls = !CodeGenOpts.SimplifyLibCalls; PMBuilder.DisableUnitAtATime = !CodeGenOpts.UnitAtATime; PMBuilder.DisableUnrollLoops = !CodeGenOpts.UnrollLoops; + + // In ObjC ARC mode, add the main ARC optimization passes. + if (LangOpts.ObjCAutoRefCount) { + PMBuilder.addExtension(PassManagerBuilder::EP_EarlyAsPossible, + addObjCARCExpandPass); + PMBuilder.addExtension(PassManagerBuilder::EP_ScalarOptimizerLate, + addObjCARCOptPass); + } // Figure out TargetLibraryInfo. Triple TargetTriple(TheModule->getTargetTriple()); @@ -258,16 +279,16 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action, const_cast<char **>(&BackendArgs[0])); std::string FeaturesStr; - if (TargetOpts.CPU.size() || TargetOpts.Features.size()) { + if (TargetOpts.Features.size()) { SubtargetFeatures Features; - Features.setCPU(TargetOpts.CPU); for (std::vector<std::string>::const_iterator it = TargetOpts.Features.begin(), ie = TargetOpts.Features.end(); it != ie; ++it) Features.AddFeature(*it); FeaturesStr = Features.getString(); } - TargetMachine *TM = TheTarget->createTargetMachine(Triple, FeaturesStr); + TargetMachine *TM = TheTarget->createTargetMachine(Triple, TargetOpts.CPU, + FeaturesStr); if (CodeGenOpts.RelaxAll) TM->setMCRelaxAll(true); @@ -275,6 +296,8 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action, TM->setMCSaveTempLabels(true); if (CodeGenOpts.NoDwarf2CFIAsm) TM->setMCUseCFI(false); + if (CodeGenOpts.NoExecStack) + TM->setMCNoExecStack(true); // Create the code generator passes. PassManager *PM = getCodeGenPasses(); @@ -295,6 +318,13 @@ bool EmitAssemblyHelper::AddEmitPasses(BackendAction Action, CGFT = TargetMachine::CGFT_Null; else assert(Action == Backend_EmitAssembly && "Invalid action!"); + + // Add ObjC ARC final-cleanup optimizations. This is done as part of the + // "codegen" passes so that it isn't run multiple times when there is + // inlining happening. + if (LangOpts.ObjCAutoRefCount) + PM->add(createObjCARCContractPass()); + if (TM->addPassesToEmitFile(*PM, OS, CGFT, OptLevel, /*DisableVerify=*/!CodeGenOpts.VerifyModule)) { Diags.Report(diag::err_fe_unable_to_interface_with_target); @@ -357,9 +387,11 @@ void EmitAssemblyHelper::EmitAssembly(BackendAction Action, raw_ostream *OS) { } void clang::EmitBackendOutput(Diagnostic &Diags, const CodeGenOptions &CGOpts, - const TargetOptions &TOpts, Module *M, + const TargetOptions &TOpts, + const LangOptions &LOpts, + Module *M, BackendAction Action, raw_ostream *OS) { - EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, M); + EmitAssemblyHelper AsmHelper(Diags, CGOpts, TOpts, LOpts, M); AsmHelper.EmitAssembly(Action, OS); } diff --git a/lib/CodeGen/CGBlocks.cpp b/lib/CodeGen/CGBlocks.cpp index e5da703a61b2..9815d1d4ef4d 100644 --- a/lib/CodeGen/CGBlocks.cpp +++ b/lib/CodeGen/CGBlocks.cpp @@ -95,9 +95,7 @@ static llvm::Constant *buildBlockDescriptor(CodeGenModule &CGM, else elements.push_back(llvm::Constant::getNullValue(i8p)); - llvm::Constant *init = - llvm::ConstantStruct::get(CGM.getLLVMContext(), elements.data(), - elements.size(), false); + llvm::Constant *init = llvm::ConstantStruct::getAnon(elements); llvm::GlobalVariable *global = new llvm::GlobalVariable(CGM.getModule(), init->getType(), true, @@ -166,11 +164,11 @@ namespace { CharUnits Alignment; CharUnits Size; const BlockDecl::Capture *Capture; // null for 'this' - const llvm::Type *Type; + llvm::Type *Type; BlockLayoutChunk(CharUnits align, CharUnits size, const BlockDecl::Capture *capture, - const llvm::Type *type) + llvm::Type *type) : Alignment(align), Size(size), Capture(capture), Type(type) {} /// Tell the block info that this chunk has the given field index. @@ -245,7 +243,7 @@ static CharUnits getLowBit(CharUnits v) { } static void initializeForBlockHeader(CodeGenModule &CGM, CGBlockInfo &info, - llvm::SmallVectorImpl<const llvm::Type*> &elementTypes) { + llvm::SmallVectorImpl<llvm::Type*> &elementTypes) { ASTContext &C = CGM.getContext(); // The header is basically a 'struct { void *; int; int; void *; void *; }'. @@ -265,8 +263,8 @@ static void initializeForBlockHeader(CodeGenModule &CGM, CGBlockInfo &info, info.BlockSize = headerSize; assert(elementTypes.empty()); - const llvm::Type *i8p = CGM.getTypes().ConvertType(C.VoidPtrTy); - const llvm::Type *intTy = CGM.getTypes().ConvertType(C.IntTy); + llvm::Type *i8p = CGM.getTypes().ConvertType(C.VoidPtrTy); + llvm::Type *intTy = CGM.getTypes().ConvertType(C.IntTy); elementTypes.push_back(i8p); elementTypes.push_back(intTy); elementTypes.push_back(intTy); @@ -282,7 +280,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { ASTContext &C = CGM.getContext(); const BlockDecl *block = info.getBlockDecl(); - llvm::SmallVector<const llvm::Type*, 8> elementTypes; + llvm::SmallVector<llvm::Type*, 8> elementTypes; initializeForBlockHeader(CGM, info, elementTypes); if (!block->hasCaptures()) { @@ -310,7 +308,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { else thisType = cast<CXXMethodDecl>(DC)->getThisType(C); - const llvm::Type *llvmType = CGM.getTypes().ConvertType(thisType); + llvm::Type *llvmType = CGM.getTypes().ConvertType(thisType); std::pair<CharUnits,CharUnits> tinfo = CGM.getContext().getTypeInfoInChars(thisType); maxFieldAlign = std::max(maxFieldAlign, tinfo.second); @@ -330,7 +328,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { // Just use void* instead of a pointer to the byref type. QualType byRefPtrTy = C.VoidPtrTy; - const llvm::Type *llvmType = CGM.getTypes().ConvertType(byRefPtrTy); + llvm::Type *llvmType = CGM.getTypes().ConvertType(byRefPtrTy); std::pair<CharUnits,CharUnits> tinfo = CGM.getContext().getTypeInfoInChars(byRefPtrTy); maxFieldAlign = std::max(maxFieldAlign, tinfo.second); @@ -347,13 +345,23 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { continue; } - // Block pointers require copy/dispose. - if (variable->getType()->isBlockPointerType()) { - info.NeedsCopyDispose = true; + // If we have a lifetime qualifier, honor it for capture purposes. + // That includes *not* copying it if it's __unsafe_unretained. + if (Qualifiers::ObjCLifetime lifetime + = variable->getType().getObjCLifetime()) { + switch (lifetime) { + case Qualifiers::OCL_None: llvm_unreachable("impossible"); + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + info.NeedsCopyDispose = true; + } - // So do Objective-C pointers. - } else if (variable->getType()->isObjCObjectPointerType() || - C.isObjCNSObjectType(variable->getType())) { + // Block pointers require copy/dispose. So do Objective-C pointers. + } else if (variable->getType()->isObjCRetainableType()) { info.NeedsCopyDispose = true; // So do types that require non-trivial copy construction. @@ -376,7 +384,7 @@ static void computeBlockInfo(CodeGenModule &CGM, CGBlockInfo &info) { CharUnits align = C.getDeclAlign(variable); maxFieldAlign = std::max(maxFieldAlign, align); - const llvm::Type *llvmType = + llvm::Type *llvmType = CGM.getTypes().ConvertTypeForMem(variable->getType()); layout.push_back(BlockLayoutChunk(align, size, &*ci, llvmType)); @@ -591,6 +599,11 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) { // Otherwise, fake up a POD copy into the block field. } else { + // Fake up a new variable so that EmitScalarInit doesn't think + // we're referring to the variable in its own initializer. + ImplicitParamDecl blockFieldPseudoVar(/*DC*/ 0, SourceLocation(), + /*name*/ 0, type); + // We use one of these or the other depending on whether the // reference is nested. DeclRefExpr notNested(const_cast<VarDecl*>(variable), type, VK_LValue, @@ -603,15 +616,37 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) { ImplicitCastExpr l2r(ImplicitCastExpr::OnStack, type, CK_LValueToRValue, declRef, VK_RValue); - EmitExprAsInit(&l2r, variable, blockField, - getContext().getDeclAlign(variable), + EmitExprAsInit(&l2r, &blockFieldPseudoVar, + LValue::MakeAddr(blockField, type, + getContext().getDeclAlign(variable) + .getQuantity(), + getContext()), /*captured by init*/ false); } // Push a destructor if necessary. The semantics for when this // actually gets run are really obscure. - if (!ci->isByRef() && CGM.getLangOptions().CPlusPlus) - PushDestructorCleanup(type, blockField); + if (!ci->isByRef()) { + switch (QualType::DestructionKind dtorKind = type.isDestructedType()) { + case QualType::DK_none: + break; + + // Block captures count as local values and have imprecise semantics. + // They also can't be arrays, so need to worry about that. + case QualType::DK_objc_strong_lifetime: { + // This local is a GCC and MSVC compiler workaround. + Destroyer *destroyer = &destroyARCStrongImprecise; + pushDestroy(getCleanupKind(dtorKind), blockField, type, + *destroyer, /*useEHCleanupForArray*/ false); + break; + } + + case QualType::DK_objc_weak_lifetime: + case QualType::DK_cxx_destructor: + pushDestroy(dtorKind, blockField, type); + break; + } + } } // Cast to the converted block-pointer type, which happens (somewhat @@ -624,11 +659,11 @@ llvm::Value *CodeGenFunction::EmitBlockLiteral(const BlockExpr *blockExpr) { } -const llvm::Type *CodeGenModule::getBlockDescriptorType() { +llvm::Type *CodeGenModule::getBlockDescriptorType() { if (BlockDescriptorType) return BlockDescriptorType; - const llvm::Type *UnsignedLongTy = + llvm::Type *UnsignedLongTy = getTypes().ConvertType(getContext().UnsignedLongTy); // struct __block_descriptor { @@ -645,24 +680,20 @@ const llvm::Type *CodeGenModule::getBlockDescriptorType() { // const char *signature; // the block signature // const char *layout; // reserved // }; - BlockDescriptorType = llvm::StructType::get(UnsignedLongTy->getContext(), - UnsignedLongTy, - UnsignedLongTy, - NULL); - - getModule().addTypeName("struct.__block_descriptor", - BlockDescriptorType); + BlockDescriptorType = + llvm::StructType::createNamed("struct.__block_descriptor", + UnsignedLongTy, UnsignedLongTy, NULL); // Now form a pointer to that. BlockDescriptorType = llvm::PointerType::getUnqual(BlockDescriptorType); return BlockDescriptorType; } -const llvm::Type *CodeGenModule::getGenericBlockLiteralType() { +llvm::Type *CodeGenModule::getGenericBlockLiteralType() { if (GenericBlockLiteralType) return GenericBlockLiteralType; - const llvm::Type *BlockDescPtrTy = getBlockDescriptorType(); + llvm::Type *BlockDescPtrTy = getBlockDescriptorType(); // struct __block_literal_generic { // void *__isa; @@ -671,16 +702,14 @@ const llvm::Type *CodeGenModule::getGenericBlockLiteralType() { // void (*__invoke)(void *); // struct __block_descriptor *__descriptor; // }; - GenericBlockLiteralType = llvm::StructType::get(getLLVMContext(), - VoidPtrTy, - IntTy, - IntTy, - VoidPtrTy, - BlockDescPtrTy, - NULL); - - getModule().addTypeName("struct.__block_literal_generic", - GenericBlockLiteralType); + GenericBlockLiteralType = + llvm::StructType::createNamed("struct.__block_literal_generic", + VoidPtrTy, + IntTy, + IntTy, + VoidPtrTy, + BlockDescPtrTy, + NULL); return GenericBlockLiteralType; } @@ -822,9 +851,7 @@ static llvm::Constant *buildGlobalBlock(CodeGenModule &CGM, // Descriptor fields[4] = buildBlockDescriptor(CGM, blockInfo); - llvm::Constant *init = - llvm::ConstantStruct::get(CGM.getLLVMContext(), fields, BlockHeaderSize, - /*packed*/ false); + llvm::Constant *init = llvm::ConstantStruct::getAnon(fields); llvm::GlobalVariable *literal = new llvm::GlobalVariable(CGM.getModule(), @@ -1023,8 +1050,6 @@ CodeGenFunction::GenerateBlockFunction(GlobalDecl GD, - - llvm::Constant * CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { ASTContext &C = getContext(); @@ -1084,21 +1109,40 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { if (capture.isConstant()) continue; const Expr *copyExpr = ci->getCopyExpr(); - unsigned flags = 0; + BlockFieldFlags flags; + + bool isARCWeakCapture = false; if (copyExpr) { assert(!ci->isByRef()); // don't bother computing flags + } else if (ci->isByRef()) { flags = BLOCK_FIELD_IS_BYREF; - if (type.isObjCGCWeak()) flags |= BLOCK_FIELD_IS_WEAK; - } else if (type->isBlockPointerType()) { - flags = BLOCK_FIELD_IS_BLOCK; - } else if (type->isObjCObjectPointerType() || C.isObjCNSObjectType(type)) { + if (type.isObjCGCWeak()) + flags |= BLOCK_FIELD_IS_WEAK; + + } else if (type->isObjCRetainableType()) { flags = BLOCK_FIELD_IS_OBJECT; - } + if (type->isBlockPointerType()) + flags = BLOCK_FIELD_IS_BLOCK; + + // Special rules for ARC captures: + if (getLangOptions().ObjCAutoRefCount) { + Qualifiers qs = type.getQualifiers(); - if (!copyExpr && !flags) continue; + // Don't generate special copy logic for a captured object + // unless it's __strong or __weak. + if (!qs.hasStrongOrWeakObjCLifetime()) + continue; + + // Support __weak direct captures. + if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) + isARCWeakCapture = true; + } + } else { + continue; + } unsigned index = capture.getIndex(); llvm::Value *srcField = Builder.CreateStructGEP(src, index); @@ -1107,12 +1151,14 @@ CodeGenFunction::GenerateCopyHelperFunction(const CGBlockInfo &blockInfo) { // If there's an explicit copy expression, we do that. if (copyExpr) { EmitSynthesizedCXXCopyCtor(dstField, srcField, copyExpr); + } else if (isARCWeakCapture) { + EmitARCCopyWeak(dstField, srcField); } else { llvm::Value *srcValue = Builder.CreateLoad(srcField, "blockcopy.src"); srcValue = Builder.CreateBitCast(srcValue, VoidPtrTy); llvm::Value *dstAddr = Builder.CreateBitCast(dstField, VoidPtrTy); Builder.CreateCall3(CGM.getBlockObjectAssign(), dstAddr, srcValue, - llvm::ConstantInt::get(Int32Ty, flags)); + llvm::ConstantInt::get(Int32Ty, flags.getBitMask())); } } @@ -1176,20 +1222,37 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { BlockFieldFlags flags; const CXXDestructorDecl *dtor = 0; + bool isARCWeakCapture = false; + if (ci->isByRef()) { flags = BLOCK_FIELD_IS_BYREF; - if (type.isObjCGCWeak()) flags |= BLOCK_FIELD_IS_WEAK; - } else if (type->isBlockPointerType()) { - flags = BLOCK_FIELD_IS_BLOCK; - } else if (type->isObjCObjectPointerType() || C.isObjCNSObjectType(type)) { + if (type.isObjCGCWeak()) + flags |= BLOCK_FIELD_IS_WEAK; + } else if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) { + if (record->hasTrivialDestructor()) + continue; + dtor = record->getDestructor(); + } else if (type->isObjCRetainableType()) { flags = BLOCK_FIELD_IS_OBJECT; - } else if (C.getLangOptions().CPlusPlus) { - if (const CXXRecordDecl *record = type->getAsCXXRecordDecl()) - if (!record->hasTrivialDestructor()) - dtor = record->getDestructor(); - } + if (type->isBlockPointerType()) + flags = BLOCK_FIELD_IS_BLOCK; - if (!dtor && flags.empty()) continue; + // Special rules for ARC captures. + if (getLangOptions().ObjCAutoRefCount) { + Qualifiers qs = type.getQualifiers(); + + // Don't generate special dispose logic for a captured object + // unless it's __strong or __weak. + if (!qs.hasStrongOrWeakObjCLifetime()) + continue; + + // Support __weak direct captures. + if (qs.getObjCLifetime() == Qualifiers::OCL_Weak) + isARCWeakCapture = true; + } + } else { + continue; + } unsigned index = capture.getIndex(); llvm::Value *srcField = Builder.CreateStructGEP(src, index); @@ -1198,6 +1261,10 @@ CodeGenFunction::GenerateDestroyHelperFunction(const CGBlockInfo &blockInfo) { if (dtor) { PushDestructorCleanup(dtor, srcField); + // If this is a __weak capture, emit the release directly. + } else if (isARCWeakCapture) { + EmitARCDestroyWeak(srcField); + // Otherwise we call _Block_object_dispose. It wouldn't be too // hard to just emit this as a cleanup if we wanted to make sure // that things were done in reverse. @@ -1251,6 +1318,55 @@ public: } }; +/// Emits the copy/dispose helpers for an ARC __block __weak variable. +class ARCWeakByrefHelpers : public CodeGenModule::ByrefHelpers { +public: + ARCWeakByrefHelpers(CharUnits alignment) : ByrefHelpers(alignment) {} + + void emitCopy(CodeGenFunction &CGF, llvm::Value *destField, + llvm::Value *srcField) { + CGF.EmitARCMoveWeak(destField, srcField); + } + + void emitDispose(CodeGenFunction &CGF, llvm::Value *field) { + CGF.EmitARCDestroyWeak(field); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const { + // 0 is distinguishable from all pointers and byref flags + id.AddInteger(0); + } +}; + +/// Emits the copy/dispose helpers for an ARC __block __strong variable +/// that's not of block-pointer type. +class ARCStrongByrefHelpers : public CodeGenModule::ByrefHelpers { +public: + ARCStrongByrefHelpers(CharUnits alignment) : ByrefHelpers(alignment) {} + + void emitCopy(CodeGenFunction &CGF, llvm::Value *destField, + llvm::Value *srcField) { + // Do a "move" by copying the value and then zeroing out the old + // variable. + + llvm::Value *value = CGF.Builder.CreateLoad(srcField); + llvm::Value *null = + llvm::ConstantPointerNull::get(cast<llvm::PointerType>(value->getType())); + CGF.Builder.CreateStore(value, destField); + CGF.Builder.CreateStore(null, srcField); + } + + void emitDispose(CodeGenFunction &CGF, llvm::Value *field) { + llvm::Value *value = CGF.Builder.CreateLoad(field); + CGF.EmitARCRelease(value, /*precise*/ false); + } + + void profileImpl(llvm::FoldingSetNodeID &id) const { + // 1 is distinguishable from all pointers and byref flags + id.AddInteger(1); + } +}; + /// Emits the copy/dispose helpers for a __block variable with a /// nontrivial copy constructor or destructor. class CXXByrefHelpers : public CodeGenModule::ByrefHelpers { @@ -1318,6 +1434,7 @@ generateByrefCopyHelper(CodeGenFunction &CGF, SC_Static, SC_None, false, true); + CGF.StartFunction(FD, R, Fn, FI, args, SourceLocation()); if (byrefInfo.needsCopy()) { @@ -1449,6 +1566,52 @@ CodeGenFunction::buildByrefHelpers(const llvm::StructType &byrefType, return ::buildByrefHelpers(CGM, byrefType, byrefInfo); } + // Otherwise, if we don't have a retainable type, there's nothing to do. + // that the runtime does extra copies. + if (!type->isObjCRetainableType()) return 0; + + Qualifiers qs = type.getQualifiers(); + + // If we have lifetime, that dominates. + if (Qualifiers::ObjCLifetime lifetime = qs.getObjCLifetime()) { + assert(getLangOptions().ObjCAutoRefCount); + + switch (lifetime) { + case Qualifiers::OCL_None: llvm_unreachable("impossible"); + + // These are just bits as far as the runtime is concerned. + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + return 0; + + // Tell the runtime that this is ARC __weak, called by the + // byref routines. + case Qualifiers::OCL_Weak: { + ARCWeakByrefHelpers byrefInfo(emission.Alignment); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + } + + // ARC __strong __block variables need to be retained. + case Qualifiers::OCL_Strong: + // Block-pointers need to be _Block_copy'ed, so we let the + // runtime be in charge. But we can't use the code below + // because we don't want to set BYREF_CALLER, which will + // just make the runtime ignore us. + if (type->isBlockPointerType()) { + BlockFieldFlags flags = BLOCK_FIELD_IS_BLOCK; + ObjectByrefHelpers byrefInfo(emission.Alignment, flags); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + + // Otherwise, we transfer ownership of the retain from the stack + // to the heap. + } else { + ARCStrongByrefHelpers byrefInfo(emission.Alignment); + return ::buildByrefHelpers(CGM, byrefType, byrefInfo); + } + } + llvm_unreachable("fell out of lifetime switch!"); + } + BlockFieldFlags flags; if (type->isBlockPointerType()) { flags |= BLOCK_FIELD_IS_BLOCK; @@ -1502,15 +1665,17 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const VarDecl *D) { QualType Ty = D->getType(); - llvm::SmallVector<const llvm::Type *, 8> types; + llvm::SmallVector<llvm::Type *, 8> types; - llvm::PATypeHolder ByRefTypeHolder = llvm::OpaqueType::get(getLLVMContext()); + llvm::StructType *ByRefType = + llvm::StructType::createNamed(getLLVMContext(), + "struct.__block_byref_" + D->getNameAsString()); // void *__isa; types.push_back(Int8PtrTy); // void *__forwarding; - types.push_back(llvm::PointerType::getUnqual(ByRefTypeHolder)); + types.push_back(llvm::PointerType::getUnqual(ByRefType)); // int32_t __flags; types.push_back(Int32Ty); @@ -1545,7 +1710,7 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const VarDecl *D) { unsigned NumPaddingBytes = AlignedOffsetInBytes - CurrentOffsetInBytes; if (NumPaddingBytes > 0) { - const llvm::Type *Ty = llvm::Type::getInt8Ty(getLLVMContext()); + llvm::Type *Ty = llvm::Type::getInt8Ty(getLLVMContext()); // FIXME: We need a sema error for alignment larger than the minimum of // the maximal stack alignment and the alignment of malloc on the system. if (NumPaddingBytes > 1) @@ -1561,13 +1726,9 @@ const llvm::Type *CodeGenFunction::BuildByRefType(const VarDecl *D) { // T x; types.push_back(ConvertTypeForMem(Ty)); - const llvm::Type *T = llvm::StructType::get(getLLVMContext(), types, Packed); - - cast<llvm::OpaqueType>(ByRefTypeHolder.get())->refineAbstractTypeTo(T); - CGM.getModule().addTypeName("struct.__block_byref_" + D->getNameAsString(), - ByRefTypeHolder.get()); + ByRefType->setBody(types, Packed); - Info.first = ByRefTypeHolder.get(); + Info.first = ByRefType; Info.second = types.size() - 1; @@ -1638,7 +1799,8 @@ namespace { llvm::Value *Addr; CallBlockRelease(llvm::Value *Addr) : Addr(Addr) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { + // Should we be passing FIELD_IS_WEAK here? CGF.BuildBlockRelease(Addr, BLOCK_FIELD_IS_BYREF); } }; diff --git a/lib/CodeGen/CGBlocks.h b/lib/CodeGen/CGBlocks.h index 9bd18e5fde74..4d8dead2be8c 100644 --- a/lib/CodeGen/CGBlocks.h +++ b/lib/CodeGen/CGBlocks.h @@ -17,7 +17,6 @@ #include "CodeGenTypes.h" #include "clang/AST/Type.h" #include "llvm/Module.h" -#include "llvm/ADT/SmallVector.h" #include "clang/Basic/TargetInfo.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Expr.h" @@ -89,7 +88,7 @@ enum BlockFieldFlag_t { variable */ BLOCK_FIELD_IS_WEAK = 0x10, /* declared __weak, only used in byref copy helpers */ - + BLOCK_FIELD_IS_ARC = 0x40, /* field has ARC-specific semantics */ BLOCK_BYREF_CALLER = 128, /* called from __block (byref) copy/dispose support routines */ BLOCK_BYREF_CURRENT_MAX = 256 diff --git a/lib/CodeGen/CGBuiltin.cpp b/lib/CodeGen/CGBuiltin.cpp index 14bebaf3c1cb..1566bd9e6697 100644 --- a/lib/CodeGen/CGBuiltin.cpp +++ b/lib/CodeGen/CGBuiltin.cpp @@ -22,6 +22,7 @@ #include "clang/Basic/TargetBuiltins.h" #include "llvm/Intrinsics.h" #include "llvm/Target/TargetData.h" + using namespace clang; using namespace CodeGen; using namespace llvm; @@ -37,8 +38,7 @@ static void EmitMemoryBarrier(CodeGenFunction &CGF, StoreLoad ? True : False, StoreStore ? True : False, Device ? True : False }; - CGF.Builder.CreateCall(CGF.CGM.getIntrinsic(Intrinsic::memory_barrier), - C, C + 5); + CGF.Builder.CreateCall(CGF.CGM.getIntrinsic(Intrinsic::memory_barrier), C); } /// Emit the conversions required to turn the given value into an @@ -68,14 +68,14 @@ static Value *EmitFromInt(CodeGenFunction &CGF, llvm::Value *V, // The atomic builtins are also full memory barriers. This is a utility for // wrapping a call to the builtins with memory barriers. static Value *EmitCallWithBarrier(CodeGenFunction &CGF, Value *Fn, - Value **ArgBegin, Value **ArgEnd) { + ArrayRef<Value *> Args) { // FIXME: We need a target hook for whether this applies to device memory or // not. bool Device = true; // Create barriers both before and after the call. EmitMemoryBarrier(CGF, true, true, true, true, Device); - Value *Result = CGF.Builder.CreateCall(Fn, ArgBegin, ArgEnd); + Value *Result = CGF.Builder.CreateCall(Fn, Args); EmitMemoryBarrier(CGF, true, true, true, true, Device); return Result; } @@ -94,13 +94,13 @@ static RValue EmitBinaryAtomic(CodeGenFunction &CGF, unsigned AddrSpace = cast<llvm::PointerType>(DestPtr->getType())->getAddressSpace(); - const llvm::IntegerType *IntType = + llvm::IntegerType *IntType = llvm::IntegerType::get(CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - const llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); + llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); - const llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; - llvm::Value *AtomF = CGF.CGM.getIntrinsic(Id, IntrinsicTypes, 2); + llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; + llvm::Value *AtomF = CGF.CGM.getIntrinsic(Id, IntrinsicTypes); llvm::Value *Args[2]; Args[0] = CGF.Builder.CreateBitCast(DestPtr, IntPtrType); @@ -108,7 +108,7 @@ static RValue EmitBinaryAtomic(CodeGenFunction &CGF, const llvm::Type *ValueType = Args[1]->getType(); Args[1] = EmitToInt(CGF, Args[1], T, IntType); - llvm::Value *Result = EmitCallWithBarrier(CGF, AtomF, Args, Args + 2); + llvm::Value *Result = EmitCallWithBarrier(CGF, AtomF, Args); Result = EmitFromInt(CGF, Result, T, ValueType); return RValue::get(Result); } @@ -129,13 +129,13 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF, unsigned AddrSpace = cast<llvm::PointerType>(DestPtr->getType())->getAddressSpace(); - const llvm::IntegerType *IntType = + llvm::IntegerType *IntType = llvm::IntegerType::get(CGF.getLLVMContext(), CGF.getContext().getTypeSize(T)); - const llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); + llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); - const llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; - llvm::Value *AtomF = CGF.CGM.getIntrinsic(Id, IntrinsicTypes, 2); + llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; + llvm::Value *AtomF = CGF.CGM.getIntrinsic(Id, IntrinsicTypes); llvm::Value *Args[2]; Args[1] = CGF.EmitScalarExpr(E->getArg(1)); @@ -143,7 +143,7 @@ static RValue EmitBinaryAtomicPost(CodeGenFunction &CGF, Args[1] = EmitToInt(CGF, Args[1], T, IntType); Args[0] = CGF.Builder.CreateBitCast(DestPtr, IntPtrType); - llvm::Value *Result = EmitCallWithBarrier(CGF, AtomF, Args, Args + 2); + llvm::Value *Result = EmitCallWithBarrier(CGF, AtomF, Args); Result = CGF.Builder.CreateBinOp(Op, Result, Args[1]); Result = EmitFromInt(CGF, Result, T, ValueType); return RValue::get(Result); @@ -164,7 +164,8 @@ static Value *EmitFAbs(CodeGenFunction &CGF, Value *V, QualType ValTy) { } // The prototype is something that takes and returns whatever V's type is. - llvm::FunctionType *FT = llvm::FunctionType::get(V->getType(), V->getType(), + llvm::Type *ArgTys[] = { V->getType() }; + llvm::FunctionType *FT = llvm::FunctionType::get(V->getType(), ArgTys, false); llvm::Value *Fn = CGF.CGM.CreateRuntimeFunction(FT, FnName); @@ -232,8 +233,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__builtin_ctzll: { Value *ArgValue = EmitScalarExpr(E->getArg(0)); - const llvm::Type *ArgType = ArgValue->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::cttz, &ArgType, 1); + llvm::Type *ArgType = ArgValue->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType); const llvm::Type *ResultType = ConvertType(E->getType()); Value *Result = Builder.CreateCall(F, ArgValue, "tmp"); @@ -247,8 +248,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__builtin_clzll: { Value *ArgValue = EmitScalarExpr(E->getArg(0)); - const llvm::Type *ArgType = ArgValue->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::ctlz, &ArgType, 1); + llvm::Type *ArgType = ArgValue->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::ctlz, ArgType); const llvm::Type *ResultType = ConvertType(E->getType()); Value *Result = Builder.CreateCall(F, ArgValue, "tmp"); @@ -263,8 +264,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, // ffs(x) -> x ? cttz(x) + 1 : 0 Value *ArgValue = EmitScalarExpr(E->getArg(0)); - const llvm::Type *ArgType = ArgValue->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::cttz, &ArgType, 1); + llvm::Type *ArgType = ArgValue->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::cttz, ArgType); const llvm::Type *ResultType = ConvertType(E->getType()); Value *Tmp = Builder.CreateAdd(Builder.CreateCall(F, ArgValue, "tmp"), @@ -283,8 +284,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, // parity(x) -> ctpop(x) & 1 Value *ArgValue = EmitScalarExpr(E->getArg(0)); - const llvm::Type *ArgType = ArgValue->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::ctpop, &ArgType, 1); + llvm::Type *ArgType = ArgValue->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::ctpop, ArgType); const llvm::Type *ResultType = ConvertType(E->getType()); Value *Tmp = Builder.CreateCall(F, ArgValue, "tmp"); @@ -300,8 +301,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__builtin_popcountll: { Value *ArgValue = EmitScalarExpr(E->getArg(0)); - const llvm::Type *ArgType = ArgValue->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::ctpop, &ArgType, 1); + llvm::Type *ArgType = ArgValue->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::ctpop, ArgType); const llvm::Type *ResultType = ConvertType(E->getType()); Value *Result = Builder.CreateCall(F, ArgValue, "tmp"); @@ -311,25 +312,27 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, return RValue::get(Result); } case Builtin::BI__builtin_expect: { - // FIXME: pass expect through to LLVM Value *ArgValue = EmitScalarExpr(E->getArg(0)); - if (E->getArg(1)->HasSideEffects(getContext())) - (void)EmitScalarExpr(E->getArg(1)); - return RValue::get(ArgValue); + llvm::Type *ArgType = ArgValue->getType(); + + Value *FnExpect = CGM.getIntrinsic(Intrinsic::expect, ArgType); + Value *ExpectedValue = EmitScalarExpr(E->getArg(1)); + + Value *Result = Builder.CreateCall2(FnExpect, ArgValue, ExpectedValue, + "expval"); + return RValue::get(Result); } case Builtin::BI__builtin_bswap32: case Builtin::BI__builtin_bswap64: { Value *ArgValue = EmitScalarExpr(E->getArg(0)); - const llvm::Type *ArgType = ArgValue->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::bswap, &ArgType, 1); + llvm::Type *ArgType = ArgValue->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::bswap, ArgType); return RValue::get(Builder.CreateCall(F, ArgValue, "tmp")); } case Builtin::BI__builtin_object_size: { // We pass this builtin onto the optimizer so that it can // figure out the object size in more complex cases. - const llvm::Type *ResType[] = { - ConvertType(E->getType()) - }; + llvm::Type *ResType = ConvertType(E->getType()); // LLVM only supports 0 and 2, make sure that we pass along that // as a boolean. @@ -339,7 +342,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, uint64_t val = CI->getZExtValue(); CI = ConstantInt::get(Builder.getInt1Ty(), (val & 0x2) >> 1); - Value *F = CGM.getIntrinsic(Intrinsic::objectsize, ResType, 1); + Value *F = CGM.getIntrinsic(Intrinsic::objectsize, ResType); return RValue::get(Builder.CreateCall2(F, EmitScalarExpr(E->getArg(0)), CI)); @@ -351,11 +354,12 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, llvm::ConstantInt::get(Int32Ty, 0); Locality = (E->getNumArgs() > 2) ? EmitScalarExpr(E->getArg(2)) : llvm::ConstantInt::get(Int32Ty, 3); - Value *F = CGM.getIntrinsic(Intrinsic::prefetch, 0, 0); - return RValue::get(Builder.CreateCall3(F, Address, RW, Locality)); + Value *Data = llvm::ConstantInt::get(Int32Ty, 1); + Value *F = CGM.getIntrinsic(Intrinsic::prefetch); + return RValue::get(Builder.CreateCall4(F, Address, RW, Locality, Data)); } case Builtin::BI__builtin_trap: { - Value *F = CGM.getIntrinsic(Intrinsic::trap, 0, 0); + Value *F = CGM.getIntrinsic(Intrinsic::trap); return RValue::get(Builder.CreateCall(F)); } case Builtin::BI__builtin_unreachable: { @@ -375,8 +379,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, case Builtin::BI__builtin_powil: { Value *Base = EmitScalarExpr(E->getArg(0)); Value *Exponent = EmitScalarExpr(E->getArg(1)); - const llvm::Type *ArgType = Base->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::powi, &ArgType, 1); + llvm::Type *ArgType = Base->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::powi, ArgType); return RValue::get(Builder.CreateCall2(F, Base, Exponent, "tmp")); } @@ -630,20 +634,20 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, // this instead of hard-coding 0, which is correct for most targets. int32_t Offset = 0; - Value *F = CGM.getIntrinsic(Intrinsic::eh_dwarf_cfa, 0, 0); + Value *F = CGM.getIntrinsic(Intrinsic::eh_dwarf_cfa); return RValue::get(Builder.CreateCall(F, llvm::ConstantInt::get(Int32Ty, Offset))); } case Builtin::BI__builtin_return_address: { Value *Depth = EmitScalarExpr(E->getArg(0)); Depth = Builder.CreateIntCast(Depth, Int32Ty, false, "tmp"); - Value *F = CGM.getIntrinsic(Intrinsic::returnaddress, 0, 0); + Value *F = CGM.getIntrinsic(Intrinsic::returnaddress); return RValue::get(Builder.CreateCall(F, Depth)); } case Builtin::BI__builtin_frame_address: { Value *Depth = EmitScalarExpr(E->getArg(0)); Depth = Builder.CreateIntCast(Depth, Int32Ty, false, "tmp"); - Value *F = CGM.getIntrinsic(Intrinsic::frameaddress, 0, 0); + Value *F = CGM.getIntrinsic(Intrinsic::frameaddress); return RValue::get(Builder.CreateCall(F, Depth)); } case Builtin::BI__builtin_extract_return_addr: { @@ -681,8 +685,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, "LLVM's __builtin_eh_return only supports 32- and 64-bit variants"); Value *F = CGM.getIntrinsic(IntTy->getBitWidth() == 32 ? Intrinsic::eh_return_i32 - : Intrinsic::eh_return_i64, - 0, 0); + : Intrinsic::eh_return_i64); Builder.CreateCall2(F, Int, Ptr); Builder.CreateUnreachable(); @@ -692,7 +695,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, return RValue::get(0); } case Builtin::BI__builtin_unwind_init: { - Value *F = CGM.getIntrinsic(Intrinsic::eh_unwind_init, 0, 0); + Value *F = CGM.getIntrinsic(Intrinsic::eh_unwind_init); return RValue::get(Builder.CreateCall(F)); } case Builtin::BI__builtin_extend_pointer: { @@ -860,13 +863,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, unsigned AddrSpace = cast<llvm::PointerType>(DestPtr->getType())->getAddressSpace(); - const llvm::IntegerType *IntType = + llvm::IntegerType *IntType = llvm::IntegerType::get(getLLVMContext(), getContext().getTypeSize(T)); - const llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); - const llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; + llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); + llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; Value *AtomF = CGM.getIntrinsic(Intrinsic::atomic_cmp_swap, - IntrinsicTypes, 2); + IntrinsicTypes); Value *Args[3]; Args[0] = Builder.CreateBitCast(DestPtr, IntPtrType); @@ -875,7 +878,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Args[1] = EmitToInt(*this, Args[1], T, IntType); Args[2] = EmitToInt(*this, EmitScalarExpr(E->getArg(2)), T, IntType); - Value *Result = EmitCallWithBarrier(*this, AtomF, Args, Args + 3); + Value *Result = EmitCallWithBarrier(*this, AtomF, Args); Result = EmitFromInt(*this, Result, T, ValueType); return RValue::get(Result); } @@ -890,13 +893,13 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, unsigned AddrSpace = cast<llvm::PointerType>(DestPtr->getType())->getAddressSpace(); - const llvm::IntegerType *IntType = + llvm::IntegerType *IntType = llvm::IntegerType::get(getLLVMContext(), getContext().getTypeSize(T)); - const llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); - const llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; + llvm::Type *IntPtrType = IntType->getPointerTo(AddrSpace); + llvm::Type *IntrinsicTypes[2] = { IntType, IntPtrType }; Value *AtomF = CGM.getIntrinsic(Intrinsic::atomic_cmp_swap, - IntrinsicTypes, 2); + IntrinsicTypes); Value *Args[3]; Args[0] = Builder.CreateBitCast(DestPtr, IntPtrType); @@ -904,7 +907,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Args[2] = EmitToInt(*this, EmitScalarExpr(E->getArg(2)), T, IntType); Value *OldVal = Args[1]; - Value *PrevVal = EmitCallWithBarrier(*this, AtomF, Args, Args + 3); + Value *PrevVal = EmitCallWithBarrier(*this, AtomF, Args); Value *Result = Builder.CreateICmpEQ(PrevVal, OldVal); // zext bool to int. Result = Builder.CreateZExt(Result, ConvertType(E->getType())); @@ -953,7 +956,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, EmitScalarExpr(E->getArg(3)), EmitScalarExpr(E->getArg(4)) }; - Builder.CreateCall(CGM.getIntrinsic(Intrinsic::memory_barrier), C, C + 5); + Builder.CreateCall(CGM.getIntrinsic(Intrinsic::memory_barrier), C); return RValue::get(0); } @@ -977,11 +980,27 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, break; Value *Base = EmitScalarExpr(E->getArg(0)); Value *Exponent = EmitScalarExpr(E->getArg(1)); - const llvm::Type *ArgType = Base->getType(); - Value *F = CGM.getIntrinsic(Intrinsic::pow, &ArgType, 1); + llvm::Type *ArgType = Base->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::pow, ArgType); return RValue::get(Builder.CreateCall2(F, Base, Exponent, "tmp")); } + case Builtin::BIfma: + case Builtin::BIfmaf: + case Builtin::BIfmal: + case Builtin::BI__builtin_fma: + case Builtin::BI__builtin_fmaf: + case Builtin::BI__builtin_fmal: { + // Rewrite fma to intrinsic. + Value *FirstArg = EmitScalarExpr(E->getArg(0)); + llvm::Type *ArgType = FirstArg->getType(); + Value *F = CGM.getIntrinsic(Intrinsic::fma, ArgType); + return RValue::get(Builder.CreateCall3(F, FirstArg, + EmitScalarExpr(E->getArg(1)), + EmitScalarExpr(E->getArg(2)), + "tmp")); + } + case Builtin::BI__builtin_signbit: case Builtin::BI__builtin_signbitf: case Builtin::BI__builtin_signbitl: { @@ -1055,7 +1074,7 @@ RValue CodeGenFunction::EmitBuiltinExpr(const FunctionDecl *FD, Args.push_back(ArgValue); } - Value *V = Builder.CreateCall(F, Args.data(), Args.data() + Args.size()); + Value *V = Builder.CreateCall(F, Args); QualType BuiltinRetType = E->getType(); const llvm::Type *RetTy = llvm::Type::getVoidTy(getLLVMContext()); @@ -1099,8 +1118,7 @@ Value *CodeGenFunction::EmitTargetBuiltinExpr(unsigned BuiltinID, } } -static const llvm::VectorType *GetNeonType(LLVMContext &C, unsigned type, - bool q) { +static llvm::VectorType *GetNeonType(LLVMContext &C, unsigned type, bool q) { switch (type) { default: break; case 0: @@ -1133,7 +1151,7 @@ Value *CodeGenFunction::EmitNeonCall(Function *F, SmallVectorImpl<Value*> &Ops, else Ops[j] = Builder.CreateBitCast(Ops[j], ai->getType(), name); - return Builder.CreateCall(F, Ops.begin(), Ops.end(), name); + return Builder.CreateCall(F, Ops, name); } Value *CodeGenFunction::EmitNeonShiftVector(Value *V, const llvm::Type *Ty, @@ -1181,8 +1199,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, const llvm::Type *Ty = CGM.getTypes().ConvertType(FD->getType()); const llvm::FunctionType *FTy = cast<llvm::FunctionType>(Ty); llvm::StringRef Name = FD->getName(); - return Builder.CreateCall(CGM.CreateRuntimeFunction(FTy, Name), - Ops.begin(), Ops.end()); + return Builder.CreateCall(CGM.CreateRuntimeFunction(FTy, Name), Ops); } if (BuiltinID == ARM::BI__builtin_arm_ldrexd) { @@ -1203,8 +1220,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, if (BuiltinID == ARM::BI__builtin_arm_strexd) { Function *F = CGM.getIntrinsic(Intrinsic::arm_strexd); - llvm::Type *STy = llvm::StructType::get(getLLVMContext(), Int32Ty, Int32Ty, - NULL); + llvm::Type *STy = llvm::StructType::get(Int32Ty, Int32Ty, NULL); Value *One = llvm::ConstantInt::get(Int32Ty, 1); Value *Tmp = Builder.CreateAlloca(Int64Ty, One, "tmp"); @@ -1232,7 +1248,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, if (BuiltinID == ARM::BI__builtin_arm_vcvtr_f || BuiltinID == ARM::BI__builtin_arm_vcvtr_d) { // Determine the overloaded type of this builtin. - const llvm::Type *Ty; + llvm::Type *Ty; if (BuiltinID == ARM::BI__builtin_arm_vcvtr_f) Ty = llvm::Type::getFloatTy(getLLVMContext()); else @@ -1243,8 +1259,8 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, unsigned Int = usgn ? Intrinsic::arm_vcvtru : Intrinsic::arm_vcvtr; // Call the appropriate intrinsic. - Function *F = CGM.getIntrinsic(Int, &Ty, 1); - return Builder.CreateCall(F, Ops.begin(), Ops.end(), "vcvtr"); + Function *F = CGM.getIntrinsic(Int, Ty); + return Builder.CreateCall(F, Ops, "vcvtr"); } // Determine the type of this overloaded NEON intrinsic. @@ -1255,8 +1271,8 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, (void)poly; // Only used in assert()s. bool rightShift = false; - const llvm::VectorType *VTy = GetNeonType(getLLVMContext(), type & 0x7, quad); - const llvm::Type *Ty = VTy; + llvm::VectorType *VTy = GetNeonType(getLLVMContext(), type & 0x7, quad); + llvm::Type *Ty = VTy; if (!Ty) return 0; @@ -1266,13 +1282,13 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vabd_v: case ARM::BI__builtin_neon_vabdq_v: Int = usgn ? Intrinsic::arm_neon_vabdu : Intrinsic::arm_neon_vabds; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vabd"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vabd"); case ARM::BI__builtin_neon_vabs_v: case ARM::BI__builtin_neon_vabsq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vabs, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vabs, Ty), Ops, "vabs"); case ARM::BI__builtin_neon_vaddhn_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vaddhn, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vaddhn, Ty), Ops, "vaddhn"); case ARM::BI__builtin_neon_vcale_v: std::swap(Ops[0], Ops[1]); @@ -1300,17 +1316,17 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vcls_v: case ARM::BI__builtin_neon_vclsq_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vcls, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vcls, Ty); return EmitNeonCall(F, Ops, "vcls"); } case ARM::BI__builtin_neon_vclz_v: case ARM::BI__builtin_neon_vclzq_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vclz, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vclz, Ty); return EmitNeonCall(F, Ops, "vclz"); } case ARM::BI__builtin_neon_vcnt_v: case ARM::BI__builtin_neon_vcntq_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vcnt, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vcnt, Ty); return EmitNeonCall(F, Ops, "vcnt"); } case ARM::BI__builtin_neon_vcvt_f16_v: { @@ -1340,18 +1356,18 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vcvt_n_f32_v: case ARM::BI__builtin_neon_vcvtq_n_f32_v: { - const llvm::Type *Tys[2] = { GetNeonType(getLLVMContext(), 4, quad), Ty }; + llvm::Type *Tys[2] = { GetNeonType(getLLVMContext(), 4, quad), Ty }; Int = usgn ? Intrinsic::arm_neon_vcvtfxu2fp : Intrinsic::arm_neon_vcvtfxs2fp; - Function *F = CGM.getIntrinsic(Int, Tys, 2); + Function *F = CGM.getIntrinsic(Int, Tys); return EmitNeonCall(F, Ops, "vcvt_n"); } case ARM::BI__builtin_neon_vcvt_n_s32_v: case ARM::BI__builtin_neon_vcvt_n_u32_v: case ARM::BI__builtin_neon_vcvtq_n_s32_v: case ARM::BI__builtin_neon_vcvtq_n_u32_v: { - const llvm::Type *Tys[2] = { Ty, GetNeonType(getLLVMContext(), 4, quad) }; + llvm::Type *Tys[2] = { Ty, GetNeonType(getLLVMContext(), 4, quad) }; Int = usgn ? Intrinsic::arm_neon_vcvtfp2fxu : Intrinsic::arm_neon_vcvtfp2fxs; - Function *F = CGM.getIntrinsic(Int, Tys, 2); + Function *F = CGM.getIntrinsic(Int, Tys); return EmitNeonCall(F, Ops, "vcvt_n"); } case ARM::BI__builtin_neon_vext_v: @@ -1381,15 +1397,15 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vhadd_v: case ARM::BI__builtin_neon_vhaddq_v: Int = usgn ? Intrinsic::arm_neon_vhaddu : Intrinsic::arm_neon_vhadds; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vhadd"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vhadd"); case ARM::BI__builtin_neon_vhsub_v: case ARM::BI__builtin_neon_vhsubq_v: Int = usgn ? Intrinsic::arm_neon_vhsubu : Intrinsic::arm_neon_vhsubs; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vhsub"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vhsub"); case ARM::BI__builtin_neon_vld1_v: case ARM::BI__builtin_neon_vld1q_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vld1, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vld1, Ty), Ops, "vld1"); case ARM::BI__builtin_neon_vld1_lane_v: case ARM::BI__builtin_neon_vld1q_lane_v: @@ -1410,7 +1426,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vld2_v: case ARM::BI__builtin_neon_vld2q_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld2, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld2, Ty); Value *Align = GetPointeeAlignment(*this, E->getArg(1)); Ops[1] = Builder.CreateCall2(F, Ops[1], Align, "vld2"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); @@ -1419,7 +1435,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vld3_v: case ARM::BI__builtin_neon_vld3q_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld3, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld3, Ty); Value *Align = GetPointeeAlignment(*this, E->getArg(1)); Ops[1] = Builder.CreateCall2(F, Ops[1], Align, "vld3"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); @@ -1428,7 +1444,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vld4_v: case ARM::BI__builtin_neon_vld4q_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld4, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld4, Ty); Value *Align = GetPointeeAlignment(*this, E->getArg(1)); Ops[1] = Builder.CreateCall2(F, Ops[1], Align, "vld4"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); @@ -1437,36 +1453,42 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, } case ARM::BI__builtin_neon_vld2_lane_v: case ARM::BI__builtin_neon_vld2q_lane_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld2lane, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld2lane, Ty); Ops[2] = Builder.CreateBitCast(Ops[2], Ty); Ops[3] = Builder.CreateBitCast(Ops[3], Ty); Ops.push_back(GetPointeeAlignment(*this, E->getArg(1))); - Ops[1] = Builder.CreateCall(F, Ops.begin() + 1, Ops.end(), "vld2_lane"); + Ops[1] = Builder.CreateCall(F, + ArrayRef<Value *>(Ops.begin() + 1, Ops.end()), + "vld2_lane"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); Ops[0] = Builder.CreateBitCast(Ops[0], Ty); return Builder.CreateStore(Ops[1], Ops[0]); } case ARM::BI__builtin_neon_vld3_lane_v: case ARM::BI__builtin_neon_vld3q_lane_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld3lane, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld3lane, Ty); Ops[2] = Builder.CreateBitCast(Ops[2], Ty); Ops[3] = Builder.CreateBitCast(Ops[3], Ty); Ops[4] = Builder.CreateBitCast(Ops[4], Ty); Ops.push_back(GetPointeeAlignment(*this, E->getArg(1))); - Ops[1] = Builder.CreateCall(F, Ops.begin() + 1, Ops.end(), "vld3_lane"); + Ops[1] = Builder.CreateCall(F, + ArrayRef<Value *>(Ops.begin() + 1, Ops.end()), + "vld3_lane"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); Ops[0] = Builder.CreateBitCast(Ops[0], Ty); return Builder.CreateStore(Ops[1], Ops[0]); } case ARM::BI__builtin_neon_vld4_lane_v: case ARM::BI__builtin_neon_vld4q_lane_v: { - Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld4lane, &Ty, 1); + Function *F = CGM.getIntrinsic(Intrinsic::arm_neon_vld4lane, Ty); Ops[2] = Builder.CreateBitCast(Ops[2], Ty); Ops[3] = Builder.CreateBitCast(Ops[3], Ty); Ops[4] = Builder.CreateBitCast(Ops[4], Ty); Ops[5] = Builder.CreateBitCast(Ops[5], Ty); Ops.push_back(GetPointeeAlignment(*this, E->getArg(1))); - Ops[1] = Builder.CreateCall(F, Ops.begin() + 1, Ops.end(), "vld3_lane"); + Ops[1] = Builder.CreateCall(F, + ArrayRef<Value *>(Ops.begin() + 1, Ops.end()), + "vld3_lane"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); Ops[0] = Builder.CreateBitCast(Ops[0], Ty); return Builder.CreateStore(Ops[1], Ops[0]); @@ -1488,7 +1510,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, break; default: assert(0 && "unknown vld_dup intrinsic?"); } - Function *F = CGM.getIntrinsic(Int, &Ty, 1); + Function *F = CGM.getIntrinsic(Int, Ty); Value *Align = GetPointeeAlignment(*this, E->getArg(1)); Ops[1] = Builder.CreateCall2(F, Ops[1], Align, "vld_dup"); Ty = llvm::PointerType::getUnqual(Ops[1]->getType()); @@ -1507,7 +1529,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, break; default: assert(0 && "unknown vld_dup intrinsic?"); } - Function *F = CGM.getIntrinsic(Int, &Ty, 1); + Function *F = CGM.getIntrinsic(Int, Ty); const llvm::StructType *STy = cast<llvm::StructType>(F->getReturnType()); SmallVector<Value*, 6> Args; @@ -1518,7 +1540,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, Args.push_back(CI); Args.push_back(GetPointeeAlignment(*this, E->getArg(1))); - Ops[1] = Builder.CreateCall(F, Args.begin(), Args.end(), "vld_dup"); + Ops[1] = Builder.CreateCall(F, Args, "vld_dup"); // splat lane 0 to all elts in each vector of the result. for (unsigned i = 0, e = STy->getNumElements(); i != e; ++i) { Value *Val = Builder.CreateExtractValue(Ops[1], i); @@ -1534,11 +1556,11 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vmax_v: case ARM::BI__builtin_neon_vmaxq_v: Int = usgn ? Intrinsic::arm_neon_vmaxu : Intrinsic::arm_neon_vmaxs; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vmax"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vmax"); case ARM::BI__builtin_neon_vmin_v: case ARM::BI__builtin_neon_vminq_v: Int = usgn ? Intrinsic::arm_neon_vminu : Intrinsic::arm_neon_vmins; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vmin"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vmin"); case ARM::BI__builtin_neon_vmovl_v: { const llvm::Type *DTy =llvm::VectorType::getTruncatedElementVectorType(VTy); Ops[0] = Builder.CreateBitCast(Ops[0], DTy); @@ -1554,12 +1576,12 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vmul_v: case ARM::BI__builtin_neon_vmulq_v: assert(poly && "vmul builtin only supported for polynomial types"); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vmulp, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vmulp, Ty), Ops, "vmul"); case ARM::BI__builtin_neon_vmull_v: Int = usgn ? Intrinsic::arm_neon_vmullu : Intrinsic::arm_neon_vmulls; Int = poly ? (unsigned)Intrinsic::arm_neon_vmullp : Int; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vmull"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vmull"); case ARM::BI__builtin_neon_vpadal_v: case ARM::BI__builtin_neon_vpadalq_v: { Int = usgn ? Intrinsic::arm_neon_vpadalu : Intrinsic::arm_neon_vpadals; @@ -1567,13 +1589,13 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, unsigned EltBits = VTy->getElementType()->getPrimitiveSizeInBits(); const llvm::Type *EltTy = llvm::IntegerType::get(getLLVMContext(), EltBits / 2); - const llvm::Type *NarrowTy = + llvm::Type *NarrowTy = llvm::VectorType::get(EltTy, VTy->getNumElements() * 2); - const llvm::Type *Tys[2] = { Ty, NarrowTy }; - return EmitNeonCall(CGM.getIntrinsic(Int, Tys, 2), Ops, "vpadal"); + llvm::Type *Tys[2] = { Ty, NarrowTy }; + return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vpadal"); } case ARM::BI__builtin_neon_vpadd_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vpadd, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vpadd, Ty), Ops, "vpadd"); case ARM::BI__builtin_neon_vpaddl_v: case ARM::BI__builtin_neon_vpaddlq_v: { @@ -1581,120 +1603,120 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, // The source operand type has twice as many elements of half the size. unsigned EltBits = VTy->getElementType()->getPrimitiveSizeInBits(); const llvm::Type *EltTy = llvm::IntegerType::get(getLLVMContext(), EltBits / 2); - const llvm::Type *NarrowTy = + llvm::Type *NarrowTy = llvm::VectorType::get(EltTy, VTy->getNumElements() * 2); - const llvm::Type *Tys[2] = { Ty, NarrowTy }; - return EmitNeonCall(CGM.getIntrinsic(Int, Tys, 2), Ops, "vpaddl"); + llvm::Type *Tys[2] = { Ty, NarrowTy }; + return EmitNeonCall(CGM.getIntrinsic(Int, Tys), Ops, "vpaddl"); } case ARM::BI__builtin_neon_vpmax_v: Int = usgn ? Intrinsic::arm_neon_vpmaxu : Intrinsic::arm_neon_vpmaxs; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vpmax"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vpmax"); case ARM::BI__builtin_neon_vpmin_v: Int = usgn ? Intrinsic::arm_neon_vpminu : Intrinsic::arm_neon_vpmins; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vpmin"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vpmin"); case ARM::BI__builtin_neon_vqabs_v: case ARM::BI__builtin_neon_vqabsq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqabs, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqabs, Ty), Ops, "vqabs"); case ARM::BI__builtin_neon_vqadd_v: case ARM::BI__builtin_neon_vqaddq_v: Int = usgn ? Intrinsic::arm_neon_vqaddu : Intrinsic::arm_neon_vqadds; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqadd"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqadd"); case ARM::BI__builtin_neon_vqdmlal_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmlal, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmlal, Ty), Ops, "vqdmlal"); case ARM::BI__builtin_neon_vqdmlsl_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmlsl, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmlsl, Ty), Ops, "vqdmlsl"); case ARM::BI__builtin_neon_vqdmulh_v: case ARM::BI__builtin_neon_vqdmulhq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmulh, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmulh, Ty), Ops, "vqdmulh"); case ARM::BI__builtin_neon_vqdmull_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmull, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqdmull, Ty), Ops, "vqdmull"); case ARM::BI__builtin_neon_vqmovn_v: Int = usgn ? Intrinsic::arm_neon_vqmovnu : Intrinsic::arm_neon_vqmovns; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqmovn"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqmovn"); case ARM::BI__builtin_neon_vqmovun_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqmovnsu, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqmovnsu, Ty), Ops, "vqdmull"); case ARM::BI__builtin_neon_vqneg_v: case ARM::BI__builtin_neon_vqnegq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqneg, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqneg, Ty), Ops, "vqneg"); case ARM::BI__builtin_neon_vqrdmulh_v: case ARM::BI__builtin_neon_vqrdmulhq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqrdmulh, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqrdmulh, Ty), Ops, "vqrdmulh"); case ARM::BI__builtin_neon_vqrshl_v: case ARM::BI__builtin_neon_vqrshlq_v: Int = usgn ? Intrinsic::arm_neon_vqrshiftu : Intrinsic::arm_neon_vqrshifts; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqrshl"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqrshl"); case ARM::BI__builtin_neon_vqrshrn_n_v: Int = usgn ? Intrinsic::arm_neon_vqrshiftnu : Intrinsic::arm_neon_vqrshiftns; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqrshrn_n", + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqrshrn_n", 1, true); case ARM::BI__builtin_neon_vqrshrun_n_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqrshiftnsu, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqrshiftnsu, Ty), Ops, "vqrshrun_n", 1, true); case ARM::BI__builtin_neon_vqshl_v: case ARM::BI__builtin_neon_vqshlq_v: Int = usgn ? Intrinsic::arm_neon_vqshiftu : Intrinsic::arm_neon_vqshifts; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqshl"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqshl"); case ARM::BI__builtin_neon_vqshl_n_v: case ARM::BI__builtin_neon_vqshlq_n_v: Int = usgn ? Intrinsic::arm_neon_vqshiftu : Intrinsic::arm_neon_vqshifts; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqshl_n", + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqshl_n", 1, false); case ARM::BI__builtin_neon_vqshlu_n_v: case ARM::BI__builtin_neon_vqshluq_n_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqshiftsu, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqshiftsu, Ty), Ops, "vqshlu", 1, false); case ARM::BI__builtin_neon_vqshrn_n_v: Int = usgn ? Intrinsic::arm_neon_vqshiftnu : Intrinsic::arm_neon_vqshiftns; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqshrn_n", + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqshrn_n", 1, true); case ARM::BI__builtin_neon_vqshrun_n_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqshiftnsu, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vqshiftnsu, Ty), Ops, "vqshrun_n", 1, true); case ARM::BI__builtin_neon_vqsub_v: case ARM::BI__builtin_neon_vqsubq_v: Int = usgn ? Intrinsic::arm_neon_vqsubu : Intrinsic::arm_neon_vqsubs; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vqsub"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vqsub"); case ARM::BI__builtin_neon_vraddhn_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vraddhn, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vraddhn, Ty), Ops, "vraddhn"); case ARM::BI__builtin_neon_vrecpe_v: case ARM::BI__builtin_neon_vrecpeq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrecpe, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrecpe, Ty), Ops, "vrecpe"); case ARM::BI__builtin_neon_vrecps_v: case ARM::BI__builtin_neon_vrecpsq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrecps, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrecps, Ty), Ops, "vrecps"); case ARM::BI__builtin_neon_vrhadd_v: case ARM::BI__builtin_neon_vrhaddq_v: Int = usgn ? Intrinsic::arm_neon_vrhaddu : Intrinsic::arm_neon_vrhadds; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vrhadd"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vrhadd"); case ARM::BI__builtin_neon_vrshl_v: case ARM::BI__builtin_neon_vrshlq_v: Int = usgn ? Intrinsic::arm_neon_vrshiftu : Intrinsic::arm_neon_vrshifts; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vrshl"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vrshl"); case ARM::BI__builtin_neon_vrshrn_n_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrshiftn, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrshiftn, Ty), Ops, "vrshrn_n", 1, true); case ARM::BI__builtin_neon_vrshr_n_v: case ARM::BI__builtin_neon_vrshrq_n_v: Int = usgn ? Intrinsic::arm_neon_vrshiftu : Intrinsic::arm_neon_vrshifts; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vrshr_n", 1, true); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vrshr_n", 1, true); case ARM::BI__builtin_neon_vrsqrte_v: case ARM::BI__builtin_neon_vrsqrteq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrsqrte, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrsqrte, Ty), Ops, "vrsqrte"); case ARM::BI__builtin_neon_vrsqrts_v: case ARM::BI__builtin_neon_vrsqrtsq_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrsqrts, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrsqrts, Ty), Ops, "vrsqrts"); case ARM::BI__builtin_neon_vrsra_n_v: case ARM::BI__builtin_neon_vrsraq_n_v: @@ -1702,10 +1724,10 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, Ops[1] = Builder.CreateBitCast(Ops[1], Ty); Ops[2] = EmitNeonShiftVector(Ops[2], Ty, true); Int = usgn ? Intrinsic::arm_neon_vrshiftu : Intrinsic::arm_neon_vrshifts; - Ops[1] = Builder.CreateCall2(CGM.getIntrinsic(Int, &Ty, 1), Ops[1], Ops[2]); + Ops[1] = Builder.CreateCall2(CGM.getIntrinsic(Int, Ty), Ops[1], Ops[2]); return Builder.CreateAdd(Ops[0], Ops[1], "vrsra_n"); case ARM::BI__builtin_neon_vrsubhn_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrsubhn, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vrsubhn, Ty), Ops, "vrsubhn"); case ARM::BI__builtin_neon_vset_lane_i8: case ARM::BI__builtin_neon_vset_lane_i16: @@ -1722,16 +1744,16 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vshl_v: case ARM::BI__builtin_neon_vshlq_v: Int = usgn ? Intrinsic::arm_neon_vshiftu : Intrinsic::arm_neon_vshifts; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vshl"); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vshl"); case ARM::BI__builtin_neon_vshll_n_v: Int = usgn ? Intrinsic::arm_neon_vshiftlu : Intrinsic::arm_neon_vshiftls; - return EmitNeonCall(CGM.getIntrinsic(Int, &Ty, 1), Ops, "vshll", 1); + return EmitNeonCall(CGM.getIntrinsic(Int, Ty), Ops, "vshll", 1); case ARM::BI__builtin_neon_vshl_n_v: case ARM::BI__builtin_neon_vshlq_n_v: Ops[1] = EmitNeonShiftVector(Ops[1], Ty, false); return Builder.CreateShl(Builder.CreateBitCast(Ops[0],Ty), Ops[1], "vshl_n"); case ARM::BI__builtin_neon_vshrn_n_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vshiftn, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vshiftn, Ty), Ops, "vshrn_n", 1, true); case ARM::BI__builtin_neon_vshr_n_v: case ARM::BI__builtin_neon_vshrq_n_v: @@ -1747,7 +1769,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vsli_n_v: case ARM::BI__builtin_neon_vsliq_n_v: Ops[2] = EmitNeonShiftVector(Ops[2], Ty, rightShift); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vshiftins, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vshiftins, Ty), Ops, "vsli_n"); case ARM::BI__builtin_neon_vsra_n_v: case ARM::BI__builtin_neon_vsraq_n_v: @@ -1762,7 +1784,7 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vst1_v: case ARM::BI__builtin_neon_vst1q_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst1, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst1, Ty), Ops, ""); case ARM::BI__builtin_neon_vst1_lane_v: case ARM::BI__builtin_neon_vst1q_lane_v: @@ -1773,35 +1795,35 @@ Value *CodeGenFunction::EmitARMBuiltinExpr(unsigned BuiltinID, case ARM::BI__builtin_neon_vst2_v: case ARM::BI__builtin_neon_vst2q_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst2, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst2, Ty), Ops, ""); case ARM::BI__builtin_neon_vst2_lane_v: case ARM::BI__builtin_neon_vst2q_lane_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst2lane, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst2lane, Ty), Ops, ""); case ARM::BI__builtin_neon_vst3_v: case ARM::BI__builtin_neon_vst3q_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst3, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst3, Ty), Ops, ""); case ARM::BI__builtin_neon_vst3_lane_v: case ARM::BI__builtin_neon_vst3q_lane_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst3lane, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst3lane, Ty), Ops, ""); case ARM::BI__builtin_neon_vst4_v: case ARM::BI__builtin_neon_vst4q_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst4, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst4, Ty), Ops, ""); case ARM::BI__builtin_neon_vst4_lane_v: case ARM::BI__builtin_neon_vst4q_lane_v: Ops.push_back(GetPointeeAlignment(*this, E->getArg(0))); - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst4lane, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vst4lane, Ty), Ops, ""); case ARM::BI__builtin_neon_vsubhn_v: - return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vsubhn, &Ty, 1), + return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vsubhn, Ty), Ops, "vsubhn"); case ARM::BI__builtin_neon_vtbl1_v: return EmitNeonCall(CGM.getIntrinsic(Intrinsic::arm_neon_vtbl1), @@ -2005,7 +2027,7 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, break; } llvm::Function *F = CGM.getIntrinsic(ID); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), name); + return Builder.CreateCall(F, Ops, name); } case X86::BI__builtin_ia32_vec_init_v8qi: case X86::BI__builtin_ia32_vec_init_v4hi: @@ -2065,15 +2087,15 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, break; } llvm::Function *F = CGM.getIntrinsic(ID); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), name); + return Builder.CreateCall(F, Ops, name); } case X86::BI__builtin_ia32_cmpps: { llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_sse_cmp_ps); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), "cmpps"); + return Builder.CreateCall(F, Ops, "cmpps"); } case X86::BI__builtin_ia32_cmpss: { llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_sse_cmp_ss); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), "cmpss"); + return Builder.CreateCall(F, Ops, "cmpss"); } case X86::BI__builtin_ia32_ldmxcsr: { const llvm::Type *PtrTy = Int8PtrTy; @@ -2093,11 +2115,11 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, } case X86::BI__builtin_ia32_cmppd: { llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_sse2_cmp_pd); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), "cmppd"); + return Builder.CreateCall(F, Ops, "cmppd"); } case X86::BI__builtin_ia32_cmpsd: { llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_sse2_cmp_sd); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), "cmpsd"); + return Builder.CreateCall(F, Ops, "cmpsd"); } case X86::BI__builtin_ia32_storehps: case X86::BI__builtin_ia32_storelps: { @@ -2141,7 +2163,7 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, // create i32 constant llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_mmx_psrl_q); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + 2, "palignr"); + return Builder.CreateCall(F, ArrayRef<Value *>(&Ops[0], 2), "palignr"); } // If palignr is shifting the pair of vectors more than 32 bytes, emit zero. @@ -2171,7 +2193,7 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, // create i32 constant llvm::Function *F = CGM.getIntrinsic(Intrinsic::x86_sse2_psrl_dq); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + 2, "palignr"); + return Builder.CreateCall(F, ArrayRef<Value *>(&Ops[0], 2), "palignr"); } // If palignr is shifting the pair of vectors more than 32 bytes, emit zero. @@ -2323,7 +2345,7 @@ Value *CodeGenFunction::EmitX86BuiltinExpr(unsigned BuiltinID, break; } llvm::Function *F = CGM.getIntrinsic(ID); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), name); + return Builder.CreateCall(F, Ops, name); } } } @@ -2379,7 +2401,7 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, break; } llvm::Function *F = CGM.getIntrinsic(ID); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), ""); + return Builder.CreateCall(F, Ops, ""); } // vec_st @@ -2412,7 +2434,7 @@ Value *CodeGenFunction::EmitPPCBuiltinExpr(unsigned BuiltinID, break; } llvm::Function *F = CGM.getIntrinsic(ID); - return Builder.CreateCall(F, &Ops[0], &Ops[0] + Ops.size(), ""); + return Builder.CreateCall(F, Ops, ""); } } return 0; diff --git a/lib/CodeGen/CGCXXABI.cpp b/lib/CodeGen/CGCXXABI.cpp index 92f1c63a3829..dcc28b45cfc3 100644 --- a/lib/CodeGen/CGCXXABI.cpp +++ b/lib/CodeGen/CGCXXABI.cpp @@ -34,7 +34,7 @@ static llvm::Constant *GetBogusMemberPointer(CodeGenModule &CGM, return llvm::Constant::getNullValue(CGM.getTypes().ConvertType(T)); } -const llvm::Type * +llvm::Type * CGCXXABI::ConvertMemberPointerType(const MemberPointerType *MPT) { return CGM.getTypes().ConvertType(CGM.getContext().getPointerDiffType()); } diff --git a/lib/CodeGen/CGCXXABI.h b/lib/CodeGen/CGCXXABI.h index de4df3dcbe84..29f299a43e37 100644 --- a/lib/CodeGen/CGCXXABI.h +++ b/lib/CodeGen/CGCXXABI.h @@ -82,7 +82,7 @@ public: /// Find the LLVM type used to represent the given member pointer /// type. - virtual const llvm::Type * + virtual llvm::Type * ConvertMemberPointerType(const MemberPointerType *MPT); /// Load a member function from an object and a member function diff --git a/lib/CodeGen/CGCall.cpp b/lib/CodeGen/CGCall.cpp index 712ae89a4888..f8783ad08d00 100644 --- a/lib/CodeGen/CGCall.cpp +++ b/lib/CodeGen/CGCall.cpp @@ -1,4 +1,4 @@ -//===----- CGCall.h - Encapsulate calling convention details ----*- C++ -*-===// +//===--- CGCall.cpp - Encapsulate calling convention details ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // @@ -25,6 +25,8 @@ #include "llvm/Attributes.h" #include "llvm/Support/CallSite.h" #include "llvm/Target/TargetData.h" +#include "llvm/InlineAsm.h" +#include "llvm/Transforms/Utils/Local.h" using namespace clang; using namespace CodeGen; @@ -65,31 +67,28 @@ static CanQualType GetReturnType(QualType RetTy) { } const CGFunctionInfo & -CodeGenTypes::getFunctionInfo(CanQual<FunctionNoProtoType> FTNP, - bool IsRecursive) { +CodeGenTypes::getFunctionInfo(CanQual<FunctionNoProtoType> FTNP) { return getFunctionInfo(FTNP->getResultType().getUnqualifiedType(), llvm::SmallVector<CanQualType, 16>(), - FTNP->getExtInfo(), IsRecursive); + FTNP->getExtInfo()); } /// \param Args - contains any initial parameters besides those /// in the formal type static const CGFunctionInfo &getFunctionInfo(CodeGenTypes &CGT, llvm::SmallVectorImpl<CanQualType> &ArgTys, - CanQual<FunctionProtoType> FTP, - bool IsRecursive = false) { + CanQual<FunctionProtoType> FTP) { // FIXME: Kill copy. for (unsigned i = 0, e = FTP->getNumArgs(); i != e; ++i) ArgTys.push_back(FTP->getArgType(i)); CanQualType ResTy = FTP->getResultType().getUnqualifiedType(); - return CGT.getFunctionInfo(ResTy, ArgTys, FTP->getExtInfo(), IsRecursive); + return CGT.getFunctionInfo(ResTy, ArgTys, FTP->getExtInfo()); } const CGFunctionInfo & -CodeGenTypes::getFunctionInfo(CanQual<FunctionProtoType> FTP, - bool IsRecursive) { +CodeGenTypes::getFunctionInfo(CanQual<FunctionProtoType> FTP) { llvm::SmallVector<CanQualType, 16> ArgTys; - return ::getFunctionInfo(*this, ArgTys, FTP, IsRecursive); + return ::getFunctionInfo(*this, ArgTys, FTP); } static CallingConv getCallingConventionForDecl(const Decl *D) { @@ -189,13 +188,15 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(const ObjCMethodDecl *MD) { e = MD->param_end(); i != e; ++i) { ArgTys.push_back(Context.getCanonicalParamType((*i)->getType())); } - return getFunctionInfo(GetReturnType(MD->getResultType()), - ArgTys, - FunctionType::ExtInfo( - /*NoReturn*/ false, - /*HasRegParm*/ false, - /*RegParm*/ 0, - getCallingConventionForDecl(MD))); + + FunctionType::ExtInfo einfo; + einfo = einfo.withCallingConv(getCallingConventionForDecl(MD)); + + if (getContext().getLangOptions().ObjCAutoRefCount && + MD->hasAttr<NSReturnsRetainedAttr>()) + einfo = einfo.withProducesResult(true); + + return getFunctionInfo(GetReturnType(MD->getResultType()), ArgTys, einfo); } const CGFunctionInfo &CodeGenTypes::getFunctionInfo(GlobalDecl GD) { @@ -240,8 +241,7 @@ const CGFunctionInfo &CodeGenTypes::getNullaryFunctionInfo() { const CGFunctionInfo &CodeGenTypes::getFunctionInfo(CanQualType ResTy, const llvm::SmallVectorImpl<CanQualType> &ArgTys, - const FunctionType::ExtInfo &Info, - bool IsRecursive) { + const FunctionType::ExtInfo &Info) { #ifndef NDEBUG for (llvm::SmallVectorImpl<CanQualType>::const_iterator I = ArgTys.begin(), E = ArgTys.end(); I != E; ++I) @@ -252,8 +252,7 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(CanQualType ResTy, // Lookup or create unique function info. llvm::FoldingSetNodeID ID; - CGFunctionInfo::Profile(ID, Info, ResTy, - ArgTys.begin(), ArgTys.end()); + CGFunctionInfo::Profile(ID, Info, ResTy, ArgTys.begin(), ArgTys.end()); void *InsertPos = 0; CGFunctionInfo *FI = FunctionInfos.FindNodeOrInsertPos(ID, InsertPos); @@ -261,10 +260,14 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(CanQualType ResTy, return *FI; // Construct the function info. - FI = new CGFunctionInfo(CC, Info.getNoReturn(), Info.getHasRegParm(), Info.getRegParm(), ResTy, + FI = new CGFunctionInfo(CC, Info.getNoReturn(), Info.getProducesResult(), + Info.getHasRegParm(), Info.getRegParm(), ResTy, ArgTys.data(), ArgTys.size()); FunctionInfos.InsertNode(FI, InsertPos); + bool Inserted = FunctionsBeingProcessed.insert(FI); (void)Inserted; + assert(Inserted && "Recursively being processed?"); + // Compute ABI information. getABIInfo().computeInfo(*FI); @@ -273,30 +276,29 @@ const CGFunctionInfo &CodeGenTypes::getFunctionInfo(CanQualType ResTy, // default now. ABIArgInfo &RetInfo = FI->getReturnInfo(); if (RetInfo.canHaveCoerceToType() && RetInfo.getCoerceToType() == 0) - RetInfo.setCoerceToType(ConvertTypeRecursive(FI->getReturnType())); + RetInfo.setCoerceToType(ConvertType(FI->getReturnType())); for (CGFunctionInfo::arg_iterator I = FI->arg_begin(), E = FI->arg_end(); I != E; ++I) if (I->info.canHaveCoerceToType() && I->info.getCoerceToType() == 0) - I->info.setCoerceToType(ConvertTypeRecursive(I->type)); - - // If this is a top-level call and ConvertTypeRecursive hit unresolved pointer - // types, resolve them now. These pointers may point to this function, which - // we *just* filled in the FunctionInfo for. - if (!IsRecursive && !PointersToResolve.empty()) - HandleLateResolvedPointers(); + I->info.setCoerceToType(ConvertType(I->type)); + bool Erased = FunctionsBeingProcessed.erase(FI); (void)Erased; + assert(Erased && "Not in set?"); + return *FI; } CGFunctionInfo::CGFunctionInfo(unsigned _CallingConvention, - bool _NoReturn, bool _HasRegParm, unsigned _RegParm, + bool _NoReturn, bool returnsRetained, + bool _HasRegParm, unsigned _RegParm, CanQualType ResTy, const CanQualType *ArgTys, unsigned NumArgTys) : CallingConvention(_CallingConvention), EffectiveCallingConvention(_CallingConvention), - NoReturn(_NoReturn), HasRegParm(_HasRegParm), RegParm(_RegParm) + NoReturn(_NoReturn), ReturnsRetained(returnsRetained), + HasRegParm(_HasRegParm), RegParm(_RegParm) { NumArgs = NumArgTys; @@ -310,8 +312,7 @@ CGFunctionInfo::CGFunctionInfo(unsigned _CallingConvention, /***/ void CodeGenTypes::GetExpandedTypes(QualType type, - llvm::SmallVectorImpl<const llvm::Type*> &expandedTypes, - bool isRecursive) { + llvm::SmallVectorImpl<llvm::Type*> &expandedTypes) { const RecordType *RT = type->getAsStructureType(); assert(RT && "Can only expand structure types."); const RecordDecl *RD = RT->getDecl(); @@ -326,9 +327,9 @@ void CodeGenTypes::GetExpandedTypes(QualType type, QualType fieldType = FD->getType(); if (fieldType->isRecordType()) - GetExpandedTypes(fieldType, expandedTypes, isRecursive); + GetExpandedTypes(fieldType, expandedTypes); else - expandedTypes.push_back(ConvertType(fieldType, isRecursive)); + expandedTypes.push_back(ConvertType(fieldType)); } } @@ -352,7 +353,7 @@ CodeGenFunction::ExpandTypeFromArgs(QualType Ty, LValue LV, if (CodeGenFunction::hasAggregateLLVMType(FT)) { AI = ExpandTypeFromArgs(FT, LV, AI); } else { - EmitStoreThroughLValue(RValue::get(AI), LV, FT); + EmitStoreThroughLValue(RValue::get(AI), LV); ++AI; } } @@ -360,33 +361,6 @@ CodeGenFunction::ExpandTypeFromArgs(QualType Ty, LValue LV, return AI; } -void -CodeGenFunction::ExpandTypeToArgs(QualType Ty, RValue RV, - llvm::SmallVector<llvm::Value*, 16> &Args) { - const RecordType *RT = Ty->getAsStructureType(); - assert(RT && "Can only expand structure types."); - - RecordDecl *RD = RT->getDecl(); - assert(RV.isAggregate() && "Unexpected rvalue during struct expansion"); - llvm::Value *Addr = RV.getAggregateAddr(); - for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); - i != e; ++i) { - FieldDecl *FD = *i; - QualType FT = FD->getType(); - - // FIXME: What are the right qualifiers here? - LValue LV = EmitLValueForField(Addr, FD, 0); - if (CodeGenFunction::hasAggregateLLVMType(FT)) { - ExpandTypeToArgs(FT, RValue::getAggregate(LV.getAddress()), Args); - } else { - RValue RV = EmitLoadOfLValue(LV, FT); - assert(RV.isScalar() && - "Unexpected non-scalar rvalue during struct expansion."); - Args.push_back(RV.getScalarVal()); - } - } -} - /// EnterStructPointerForCoercedAccess - Given a struct pointer that we are /// accessing some number of bytes out of it, try to gep into the struct to get /// at its inner goodness. Dive as deep as possible without entering an element @@ -622,7 +596,7 @@ bool CodeGenModule::ReturnTypeUsesFPRet(QualType ResultType) { return false; } -const llvm::FunctionType *CodeGenTypes::GetFunctionType(GlobalDecl GD) { +llvm::FunctionType *CodeGenTypes::GetFunctionType(GlobalDecl GD) { const CGFunctionInfo &FI = getFunctionInfo(GD); // For definition purposes, don't consider a K&R function variadic. @@ -631,13 +605,16 @@ const llvm::FunctionType *CodeGenTypes::GetFunctionType(GlobalDecl GD) { cast<FunctionDecl>(GD.getDecl())->getType()->getAs<FunctionProtoType>()) Variadic = FPT->isVariadic(); - return GetFunctionType(FI, Variadic, false); + return GetFunctionType(FI, Variadic); } -const llvm::FunctionType * -CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool isVariadic, - bool isRecursive) { - llvm::SmallVector<const llvm::Type*, 8> argTypes; +llvm::FunctionType * +CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool isVariadic) { + + bool Inserted = FunctionsBeingProcessed.insert(&FI); (void)Inserted; + assert(Inserted && "Recursively being processed?"); + + llvm::SmallVector<llvm::Type*, 8> argTypes; const llvm::Type *resultType = 0; const ABIArgInfo &retAI = FI.getReturnInfo(); @@ -655,7 +632,7 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool isVariadic, resultType = llvm::Type::getVoidTy(getLLVMContext()); QualType ret = FI.getReturnType(); - const llvm::Type *ty = ConvertType(ret, isRecursive); + const llvm::Type *ty = ConvertType(ret); unsigned addressSpace = Context.getTargetAddressSpace(ret); argTypes.push_back(llvm::PointerType::get(ty, addressSpace)); break; @@ -676,7 +653,7 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool isVariadic, case ABIArgInfo::Indirect: { // indirect arguments are always on the stack, which is addr space #0. - const llvm::Type *LTy = ConvertTypeForMem(it->type, isRecursive); + const llvm::Type *LTy = ConvertTypeForMem(it->type); argTypes.push_back(LTy->getPointerTo()); break; } @@ -686,7 +663,7 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool isVariadic, // If the coerce-to type is a first class aggregate, flatten it. Either // way is semantically identical, but fast-isel and the optimizer // generally likes scalar values better than FCAs. - const llvm::Type *argType = argAI.getCoerceToType(); + llvm::Type *argType = argAI.getCoerceToType(); if (const llvm::StructType *st = dyn_cast<llvm::StructType>(argType)) { for (unsigned i = 0, e = st->getNumElements(); i != e; ++i) argTypes.push_back(st->getElementType(i)); @@ -697,11 +674,14 @@ CodeGenTypes::GetFunctionType(const CGFunctionInfo &FI, bool isVariadic, } case ABIArgInfo::Expand: - GetExpandedTypes(it->type, argTypes, isRecursive); + GetExpandedTypes(it->type, argTypes); break; } } + bool Erased = FunctionsBeingProcessed.erase(&FI); (void)Erased; + assert(Erased && "Not in set?"); + return llvm::FunctionType::get(resultType, argTypes, isVariadic); } @@ -709,16 +689,15 @@ const llvm::Type *CodeGenTypes::GetFunctionTypeForVTable(GlobalDecl GD) { const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>(); - if (!VerifyFuncTypeComplete(FPT)) { - const CGFunctionInfo *Info; - if (isa<CXXDestructorDecl>(MD)) - Info = &getFunctionInfo(cast<CXXDestructorDecl>(MD), GD.getDtorType()); - else - Info = &getFunctionInfo(MD); - return GetFunctionType(*Info, FPT->isVariadic(), false); - } - - return llvm::OpaqueType::get(getLLVMContext()); + if (!isFuncTypeConvertible(FPT)) + return llvm::StructType::get(getLLVMContext()); + + const CGFunctionInfo *Info; + if (isa<CXXDestructorDecl>(MD)) + Info = &getFunctionInfo(cast<CXXDestructorDecl>(MD), GD.getDtorType()); + else + Info = &getFunctionInfo(MD); + return GetFunctionType(*Info, FPT->isVariadic()); } void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, @@ -845,11 +824,11 @@ void CodeGenModule::ConstructAttributeList(const CGFunctionInfo &FI, continue; case ABIArgInfo::Expand: { - llvm::SmallVector<const llvm::Type*, 8> types; + llvm::SmallVector<llvm::Type*, 8> types; // FIXME: This is rather inefficient. Do we ever actually need to do // anything here? The result should be just reconstructed on the other // side, so extension should be a non-issue. - getTypes().GetExpandedTypes(ParamType, types, false); + getTypes().GetExpandedTypes(ParamType, types); Index += types.size(); continue; } @@ -1067,6 +1046,95 @@ void CodeGenFunction::EmitFunctionProlog(const CGFunctionInfo &FI, assert(AI == Fn->arg_end() && "Argument mismatch!"); } +/// Try to emit a fused autorelease of a return result. +static llvm::Value *tryEmitFusedAutoreleaseOfResult(CodeGenFunction &CGF, + llvm::Value *result) { + // We must be immediately followed the cast. + llvm::BasicBlock *BB = CGF.Builder.GetInsertBlock(); + if (BB->empty()) return 0; + if (&BB->back() != result) return 0; + + const llvm::Type *resultType = result->getType(); + + // result is in a BasicBlock and is therefore an Instruction. + llvm::Instruction *generator = cast<llvm::Instruction>(result); + + llvm::SmallVector<llvm::Instruction*,4> insnsToKill; + + // Look for: + // %generator = bitcast %type1* %generator2 to %type2* + while (llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(generator)) { + // We would have emitted this as a constant if the operand weren't + // an Instruction. + generator = cast<llvm::Instruction>(bitcast->getOperand(0)); + + // Require the generator to be immediately followed by the cast. + if (generator->getNextNode() != bitcast) + return 0; + + insnsToKill.push_back(bitcast); + } + + // Look for: + // %generator = call i8* @objc_retain(i8* %originalResult) + // or + // %generator = call i8* @objc_retainAutoreleasedReturnValue(i8* %originalResult) + llvm::CallInst *call = dyn_cast<llvm::CallInst>(generator); + if (!call) return 0; + + bool doRetainAutorelease; + + if (call->getCalledValue() == CGF.CGM.getARCEntrypoints().objc_retain) { + doRetainAutorelease = true; + } else if (call->getCalledValue() == CGF.CGM.getARCEntrypoints() + .objc_retainAutoreleasedReturnValue) { + doRetainAutorelease = false; + + // Look for an inline asm immediately preceding the call and kill it, too. + llvm::Instruction *prev = call->getPrevNode(); + if (llvm::CallInst *asmCall = dyn_cast_or_null<llvm::CallInst>(prev)) + if (asmCall->getCalledValue() + == CGF.CGM.getARCEntrypoints().retainAutoreleasedReturnValueMarker) + insnsToKill.push_back(prev); + } else { + return 0; + } + + result = call->getArgOperand(0); + insnsToKill.push_back(call); + + // Keep killing bitcasts, for sanity. Note that we no longer care + // about precise ordering as long as there's exactly one use. + while (llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(result)) { + if (!bitcast->hasOneUse()) break; + insnsToKill.push_back(bitcast); + result = bitcast->getOperand(0); + } + + // Delete all the unnecessary instructions, from latest to earliest. + for (llvm::SmallVectorImpl<llvm::Instruction*>::iterator + i = insnsToKill.begin(), e = insnsToKill.end(); i != e; ++i) + (*i)->eraseFromParent(); + + // Do the fused retain/autorelease if we were asked to. + if (doRetainAutorelease) + result = CGF.EmitARCRetainAutoreleaseReturnValue(result); + + // Cast back to the result type. + return CGF.Builder.CreateBitCast(result, resultType); +} + +/// Emit an ARC autorelease of the result of a function. +static llvm::Value *emitAutoreleaseOfResult(CodeGenFunction &CGF, + llvm::Value *result) { + // At -O0, try to emit a fused retain/autorelease. + if (CGF.shouldUseFusedARCCalls()) + if (llvm::Value *fused = tryEmitFusedAutoreleaseOfResult(CGF, result)) + return fused; + + return CGF.EmitARCAutoreleaseReturnValue(result); +} + void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI) { // Functions with no result always return void. if (ReturnValue == 0) { @@ -1134,6 +1202,16 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI) { RV = CreateCoercedLoad(V, RetAI.getCoerceToType(), *this); } + + // In ARC, end functions that return a retainable type with a call + // to objc_autoreleaseReturnValue. + if (AutoreleaseResult) { + assert(getLangOptions().ObjCAutoRefCount && + !FI.isReturnsRetained() && + RetTy->isObjCRetainableType()); + RV = emitAutoreleaseOfResult(*this, RV); + } + break; case ABIArgInfo::Ignore: @@ -1183,13 +1261,157 @@ void CodeGenFunction::EmitDelegateCallArg(CallArgList &args, return args.add(RValue::get(value), type); } +static bool isProvablyNull(llvm::Value *addr) { + return isa<llvm::ConstantPointerNull>(addr); +} + +static bool isProvablyNonNull(llvm::Value *addr) { + return isa<llvm::AllocaInst>(addr); +} + +/// Emit the actual writing-back of a writeback. +static void emitWriteback(CodeGenFunction &CGF, + const CallArgList::Writeback &writeback) { + llvm::Value *srcAddr = writeback.Address; + assert(!isProvablyNull(srcAddr) && + "shouldn't have writeback for provably null argument"); + + llvm::BasicBlock *contBB = 0; + + // If the argument wasn't provably non-null, we need to null check + // before doing the store. + bool provablyNonNull = isProvablyNonNull(srcAddr); + if (!provablyNonNull) { + llvm::BasicBlock *writebackBB = CGF.createBasicBlock("icr.writeback"); + contBB = CGF.createBasicBlock("icr.done"); + + llvm::Value *isNull = CGF.Builder.CreateIsNull(srcAddr, "icr.isnull"); + CGF.Builder.CreateCondBr(isNull, contBB, writebackBB); + CGF.EmitBlock(writebackBB); + } + + // Load the value to writeback. + llvm::Value *value = CGF.Builder.CreateLoad(writeback.Temporary); + + // Cast it back, in case we're writing an id to a Foo* or something. + value = CGF.Builder.CreateBitCast(value, + cast<llvm::PointerType>(srcAddr->getType())->getElementType(), + "icr.writeback-cast"); + + // Perform the writeback. + QualType srcAddrType = writeback.AddressType; + CGF.EmitStoreThroughLValue(RValue::get(value), + CGF.MakeAddrLValue(srcAddr, srcAddrType)); + + // Jump to the continuation block. + if (!provablyNonNull) + CGF.EmitBlock(contBB); +} + +static void emitWritebacks(CodeGenFunction &CGF, + const CallArgList &args) { + for (CallArgList::writeback_iterator + i = args.writeback_begin(), e = args.writeback_end(); i != e; ++i) + emitWriteback(CGF, *i); +} + +/// Emit an argument that's being passed call-by-writeback. That is, +/// we are passing the address of +static void emitWritebackArg(CodeGenFunction &CGF, CallArgList &args, + const ObjCIndirectCopyRestoreExpr *CRE) { + llvm::Value *srcAddr = CGF.EmitScalarExpr(CRE->getSubExpr()); + + // The dest and src types don't necessarily match in LLVM terms + // because of the crazy ObjC compatibility rules. + + const llvm::PointerType *destType = + cast<llvm::PointerType>(CGF.ConvertType(CRE->getType())); + + // If the address is a constant null, just pass the appropriate null. + if (isProvablyNull(srcAddr)) { + args.add(RValue::get(llvm::ConstantPointerNull::get(destType)), + CRE->getType()); + return; + } + + QualType srcAddrType = + CRE->getSubExpr()->getType()->castAs<PointerType>()->getPointeeType(); + + // Create the temporary. + llvm::Value *temp = CGF.CreateTempAlloca(destType->getElementType(), + "icr.temp"); + + // Zero-initialize it if we're not doing a copy-initialization. + bool shouldCopy = CRE->shouldCopy(); + if (!shouldCopy) { + llvm::Value *null = + llvm::ConstantPointerNull::get( + cast<llvm::PointerType>(destType->getElementType())); + CGF.Builder.CreateStore(null, temp); + } + + llvm::BasicBlock *contBB = 0; + + // If the address is *not* known to be non-null, we need to switch. + llvm::Value *finalArgument; + + bool provablyNonNull = isProvablyNonNull(srcAddr); + if (provablyNonNull) { + finalArgument = temp; + } else { + llvm::Value *isNull = CGF.Builder.CreateIsNull(srcAddr, "icr.isnull"); + + finalArgument = CGF.Builder.CreateSelect(isNull, + llvm::ConstantPointerNull::get(destType), + temp, "icr.argument"); + + // If we need to copy, then the load has to be conditional, which + // means we need control flow. + if (shouldCopy) { + contBB = CGF.createBasicBlock("icr.cont"); + llvm::BasicBlock *copyBB = CGF.createBasicBlock("icr.copy"); + CGF.Builder.CreateCondBr(isNull, contBB, copyBB); + CGF.EmitBlock(copyBB); + } + } + + // Perform a copy if necessary. + if (shouldCopy) { + LValue srcLV = CGF.MakeAddrLValue(srcAddr, srcAddrType); + RValue srcRV = CGF.EmitLoadOfLValue(srcLV); + assert(srcRV.isScalar()); + + llvm::Value *src = srcRV.getScalarVal(); + src = CGF.Builder.CreateBitCast(src, destType->getElementType(), + "icr.cast"); + + // Use an ordinary store, not a store-to-lvalue. + CGF.Builder.CreateStore(src, temp); + } + + // Finish the control flow if we needed it. + if (shouldCopy && !provablyNonNull) + CGF.EmitBlock(contBB); + + args.addWriteback(srcAddr, srcAddrType, temp); + args.add(RValue::get(finalArgument), CRE->getType()); +} + void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, QualType type) { + if (const ObjCIndirectCopyRestoreExpr *CRE + = dyn_cast<ObjCIndirectCopyRestoreExpr>(E)) { + assert(getContext().getLangOptions().ObjCAutoRefCount); + assert(getContext().hasSameType(E->getType(), type)); + return emitWritebackArg(*this, args, CRE); + } + if (type->isReferenceType()) return args.add(EmitReferenceBindingToExpr(E, /*InitializedDecl=*/0), type); - if (hasAggregateLLVMType(type) && isa<ImplicitCastExpr>(E) && + if (hasAggregateLLVMType(type) && !E->getType()->isAnyComplexType() && + isa<ImplicitCastExpr>(E) && cast<CastExpr>(E)->getCastKind() == CK_LValueToRValue) { LValue L = EmitLValue(cast<CastExpr>(E)->getSubExpr()); assert(L.isSimple()); @@ -1205,20 +1427,71 @@ void CodeGenFunction::EmitCallArg(CallArgList &args, const Expr *E, /// on the current state of the EH stack. llvm::CallSite CodeGenFunction::EmitCallOrInvoke(llvm::Value *Callee, - llvm::Value * const *ArgBegin, - llvm::Value * const *ArgEnd, + llvm::ArrayRef<llvm::Value *> Args, const llvm::Twine &Name) { llvm::BasicBlock *InvokeDest = getInvokeDest(); if (!InvokeDest) - return Builder.CreateCall(Callee, ArgBegin, ArgEnd, Name); + return Builder.CreateCall(Callee, Args, Name); llvm::BasicBlock *ContBB = createBasicBlock("invoke.cont"); llvm::InvokeInst *Invoke = Builder.CreateInvoke(Callee, ContBB, InvokeDest, - ArgBegin, ArgEnd, Name); + Args, Name); EmitBlock(ContBB); return Invoke; } +llvm::CallSite +CodeGenFunction::EmitCallOrInvoke(llvm::Value *Callee, + const llvm::Twine &Name) { + return EmitCallOrInvoke(Callee, llvm::ArrayRef<llvm::Value *>(), Name); +} + +static void checkArgMatches(llvm::Value *Elt, unsigned &ArgNo, + llvm::FunctionType *FTy) { + if (ArgNo < FTy->getNumParams()) + assert(Elt->getType() == FTy->getParamType(ArgNo)); + else + assert(FTy->isVarArg()); + ++ArgNo; +} + +void CodeGenFunction::ExpandTypeToArgs(QualType Ty, RValue RV, + llvm::SmallVector<llvm::Value*,16> &Args, + llvm::FunctionType *IRFuncTy) { + const RecordType *RT = Ty->getAsStructureType(); + assert(RT && "Can only expand structure types."); + + RecordDecl *RD = RT->getDecl(); + assert(RV.isAggregate() && "Unexpected rvalue during struct expansion"); + llvm::Value *Addr = RV.getAggregateAddr(); + for (RecordDecl::field_iterator i = RD->field_begin(), e = RD->field_end(); + i != e; ++i) { + FieldDecl *FD = *i; + QualType FT = FD->getType(); + + // FIXME: What are the right qualifiers here? + LValue LV = EmitLValueForField(Addr, FD, 0); + if (CodeGenFunction::hasAggregateLLVMType(FT)) { + ExpandTypeToArgs(FT, RValue::getAggregate(LV.getAddress()), + Args, IRFuncTy); + continue; + } + + RValue RV = EmitLoadOfLValue(LV); + assert(RV.isScalar() && + "Unexpected non-scalar rvalue during struct expansion."); + + // Insert a bitcast as needed. + llvm::Value *V = RV.getScalarVal(); + if (Args.size() < IRFuncTy->getNumParams() && + V->getType() != IRFuncTy->getParamType(Args.size())) + V = Builder.CreateBitCast(V, IRFuncTy->getParamType(Args.size())); + + Args.push_back(V); + } +} + + RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, llvm::Value *Callee, ReturnValueSlot ReturnValue, @@ -1233,6 +1506,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, QualType RetTy = CallInfo.getReturnType(); const ABIArgInfo &RetAI = CallInfo.getReturnInfo(); + // IRArgNo - Keep track of the argument number in the callee we're looking at. + unsigned IRArgNo = 0; + llvm::FunctionType *IRFuncTy = + cast<llvm::FunctionType>( + cast<llvm::PointerType>(Callee->getType())->getElementType()); // If the call returns a temporary with struct return, create a temporary // alloca to hold the result, unless one is given to us. @@ -1241,6 +1519,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (!Value) Value = CreateMemTemp(RetTy); Args.push_back(Value); + checkArgMatches(Value, IRArgNo, IRFuncTy); } assert(CallInfo.arg_size() == CallArgs.size() && @@ -1251,24 +1530,54 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, const ABIArgInfo &ArgInfo = info_it->info; RValue RV = I->RV; - unsigned Alignment = + unsigned TypeAlign = getContext().getTypeAlignInChars(I->Ty).getQuantity(); switch (ArgInfo.getKind()) { case ABIArgInfo::Indirect: { if (RV.isScalar() || RV.isComplex()) { // Make a temporary alloca to pass the argument. - Args.push_back(CreateMemTemp(I->Ty)); + llvm::AllocaInst *AI = CreateMemTemp(I->Ty); + if (ArgInfo.getIndirectAlign() > AI->getAlignment()) + AI->setAlignment(ArgInfo.getIndirectAlign()); + Args.push_back(AI); + if (RV.isScalar()) EmitStoreOfScalar(RV.getScalarVal(), Args.back(), false, - Alignment, I->Ty); + TypeAlign, I->Ty); else StoreComplexToAddr(RV.getComplexVal(), Args.back(), false); - } else if (I->NeedsCopy && !ArgInfo.getIndirectByVal()) { - Args.push_back(CreateMemTemp(I->Ty)); - EmitAggregateCopy(Args.back(), RV.getAggregateAddr(), I->Ty, - RV.isVolatileQualified()); + + // Validate argument match. + checkArgMatches(AI, IRArgNo, IRFuncTy); } else { - Args.push_back(RV.getAggregateAddr()); + // We want to avoid creating an unnecessary temporary+copy here; + // however, we need one in two cases: + // 1. If the argument is not byval, and we are required to copy the + // source. (This case doesn't occur on any common architecture.) + // 2. If the argument is byval, RV is not sufficiently aligned, and + // we cannot force it to be sufficiently aligned. + llvm::Value *Addr = RV.getAggregateAddr(); + unsigned Align = ArgInfo.getIndirectAlign(); + const llvm::TargetData *TD = &CGM.getTargetData(); + if ((!ArgInfo.getIndirectByVal() && I->NeedsCopy) || + (ArgInfo.getIndirectByVal() && TypeAlign < Align && + llvm::getOrEnforceKnownAlignment(Addr, Align, TD) < Align)) { + // Create an aligned temporary, and copy to it. + llvm::AllocaInst *AI = CreateMemTemp(I->Ty); + if (Align > AI->getAlignment()) + AI->setAlignment(Align); + Args.push_back(AI); + EmitAggregateCopy(AI, Addr, I->Ty, RV.isVolatileQualified()); + + // Validate argument match. + checkArgMatches(AI, IRArgNo, IRFuncTy); + } else { + // Skip the extra memcpy call. + Args.push_back(Addr); + + // Validate argument match. + checkArgMatches(Addr, IRArgNo, IRFuncTy); + } } break; } @@ -1281,10 +1590,20 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (!isa<llvm::StructType>(ArgInfo.getCoerceToType()) && ArgInfo.getCoerceToType() == ConvertType(info_it->type) && ArgInfo.getDirectOffset() == 0) { + llvm::Value *V; if (RV.isScalar()) - Args.push_back(RV.getScalarVal()); + V = RV.getScalarVal(); else - Args.push_back(Builder.CreateLoad(RV.getAggregateAddr())); + V = Builder.CreateLoad(RV.getAggregateAddr()); + + // If the argument doesn't match, perform a bitcast to coerce it. This + // can happen due to trivial type mismatches. + if (IRArgNo < IRFuncTy->getNumParams() && + V->getType() != IRFuncTy->getParamType(IRArgNo)) + V = Builder.CreateBitCast(V, IRFuncTy->getParamType(IRArgNo)); + Args.push_back(V); + + checkArgMatches(V, IRArgNo, IRFuncTy); break; } @@ -1292,7 +1611,7 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, llvm::Value *SrcPtr; if (RV.isScalar()) { SrcPtr = CreateMemTemp(I->Ty, "coerce"); - EmitStoreOfScalar(RV.getScalarVal(), SrcPtr, false, Alignment, I->Ty); + EmitStoreOfScalar(RV.getScalarVal(), SrcPtr, false, TypeAlign, I->Ty); } else if (RV.isComplex()) { SrcPtr = CreateMemTemp(I->Ty, "coerce"); StoreComplexToAddr(RV.getComplexVal(), SrcPtr, false); @@ -1321,18 +1640,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, // We don't know what we're loading from. LI->setAlignment(1); Args.push_back(LI); + + // Validate argument match. + checkArgMatches(LI, IRArgNo, IRFuncTy); } } else { // In the simple case, just pass the coerced loaded value. Args.push_back(CreateCoercedLoad(SrcPtr, ArgInfo.getCoerceToType(), *this)); + + // Validate argument match. + checkArgMatches(Args.back(), IRArgNo, IRFuncTy); } break; } case ABIArgInfo::Expand: - ExpandTypeToArgs(I->Ty, RV, Args); + ExpandTypeToArgs(I->Ty, RV, Args, IRFuncTy); + IRArgNo = Args.size(); break; } } @@ -1367,7 +1693,6 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, } } - unsigned CallingConv; CodeGen::AttributeListType AttributeList; CGM.ConstructAttributeList(CallInfo, TargetDecl, AttributeList, CallingConv); @@ -1380,11 +1705,10 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, llvm::CallSite CS; if (!InvokeDest) { - CS = Builder.CreateCall(Callee, Args.data(), Args.data()+Args.size()); + CS = Builder.CreateCall(Callee, Args); } else { llvm::BasicBlock *Cont = createBasicBlock("invoke.cont"); - CS = Builder.CreateInvoke(Callee, Cont, InvokeDest, - Args.data(), Args.data()+Args.size()); + CS = Builder.CreateInvoke(Callee, Cont, InvokeDest, Args); EmitBlock(Cont); } if (callOrInvoke) @@ -1413,6 +1737,11 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, if (Builder.isNamePreserving() && !CI->getType()->isVoidTy()) CI->setName("call"); + // Emit any writebacks immediately. Arguably this should happen + // after any return-value munging. + if (CallArgs.hasWritebacks()) + emitWritebacks(*this, CallArgs); + switch (RetAI.getKind()) { case ABIArgInfo::Indirect: { unsigned Alignment = getContext().getTypeAlignInChars(RetTy).getQuantity(); @@ -1430,8 +1759,8 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, case ABIArgInfo::Extend: case ABIArgInfo::Direct: { - if (RetAI.getCoerceToType() == ConvertType(RetTy) && - RetAI.getDirectOffset() == 0) { + llvm::Type *RetIRTy = ConvertType(RetTy); + if (RetAI.getCoerceToType() == RetIRTy && RetAI.getDirectOffset() == 0) { if (RetTy->isAnyComplexType()) { llvm::Value *Real = Builder.CreateExtractValue(CI, 0); llvm::Value *Imag = Builder.CreateExtractValue(CI, 1); @@ -1448,7 +1777,13 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, BuildAggStore(*this, CI, DestPtr, DestIsVolatile, false); return RValue::getAggregate(DestPtr); } - return RValue::get(CI); + + // If the argument doesn't match, perform a bitcast to coerce it. This + // can happen due to trivial type mismatches. + llvm::Value *V = CI; + if (V->getType() != RetIRTy) + V = Builder.CreateBitCast(V, RetIRTy); + return RValue::get(V); } llvm::Value *DestPtr = ReturnValue.getValue(); diff --git a/lib/CodeGen/CGCall.h b/lib/CodeGen/CGCall.h index 160a62eab367..343b944bf6c9 100644 --- a/lib/CodeGen/CGCall.h +++ b/lib/CodeGen/CGCall.h @@ -58,9 +58,44 @@ namespace CodeGen { class CallArgList : public llvm::SmallVector<CallArg, 16> { public: + struct Writeback { + /// The original argument. + llvm::Value *Address; + + /// The pointee type of the original argument. + QualType AddressType; + + /// The temporary alloca. + llvm::Value *Temporary; + }; + void add(RValue rvalue, QualType type, bool needscopy = false) { push_back(CallArg(rvalue, type, needscopy)); } + + void addFrom(const CallArgList &other) { + insert(end(), other.begin(), other.end()); + Writebacks.insert(Writebacks.end(), + other.Writebacks.begin(), other.Writebacks.end()); + } + + void addWriteback(llvm::Value *address, QualType addressType, + llvm::Value *temporary) { + Writeback writeback; + writeback.Address = address; + writeback.AddressType = addressType; + writeback.Temporary = temporary; + Writebacks.push_back(writeback); + } + + bool hasWritebacks() const { return !Writebacks.empty(); } + + typedef llvm::SmallVectorImpl<Writeback>::const_iterator writeback_iterator; + writeback_iterator writeback_begin() const { return Writebacks.begin(); } + writeback_iterator writeback_end() const { return Writebacks.end(); } + + private: + llvm::SmallVector<Writeback, 1> Writebacks; }; /// FunctionArgList - Type for representing both the decl and type @@ -88,6 +123,9 @@ namespace CodeGen { /// Whether this function is noreturn. bool NoReturn; + /// Whether this function is returns-retained. + bool ReturnsRetained; + unsigned NumArgs; ArgInfo *Args; @@ -100,7 +138,8 @@ namespace CodeGen { typedef ArgInfo *arg_iterator; CGFunctionInfo(unsigned CallingConvention, bool NoReturn, - bool HasRegParm, unsigned RegParm, CanQualType ResTy, + bool ReturnsRetained, bool HasRegParm, unsigned RegParm, + CanQualType ResTy, const CanQualType *ArgTys, unsigned NumArgTys); ~CGFunctionInfo() { delete[] Args; } @@ -113,6 +152,10 @@ namespace CodeGen { bool isNoReturn() const { return NoReturn; } + /// In ARR, whether this function retains its return value. This + /// is not always reliable for call sites. + bool isReturnsRetained() const { return ReturnsRetained; } + /// getCallingConvention - Return the user specified calling /// convention. unsigned getCallingConvention() const { return CallingConvention; } @@ -137,6 +180,7 @@ namespace CodeGen { void Profile(llvm::FoldingSetNodeID &ID) { ID.AddInteger(getCallingConvention()); ID.AddBoolean(NoReturn); + ID.AddBoolean(ReturnsRetained); ID.AddBoolean(HasRegParm); ID.AddInteger(RegParm); getReturnType().Profile(ID); @@ -151,6 +195,7 @@ namespace CodeGen { Iterator end) { ID.AddInteger(Info.getCC()); ID.AddBoolean(Info.getNoReturn()); + ID.AddBoolean(Info.getProducesResult()); ID.AddBoolean(Info.getHasRegParm()); ID.AddInteger(Info.getRegParm()); ResTy.Profile(ID); diff --git a/lib/CodeGen/CGClass.cpp b/lib/CodeGen/CGClass.cpp index 5725d80b7d03..7dbaaf85299f 100644 --- a/lib/CodeGen/CGClass.cpp +++ b/lib/CodeGen/CGClass.cpp @@ -329,7 +329,7 @@ namespace { CallBaseDtor(const CXXRecordDecl *Base, bool BaseIsVirtual) : BaseClass(Base), BaseIsVirtual(BaseIsVirtual) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { const CXXRecordDecl *DerivedClass = cast<CXXMethodDecl>(CGF.CurCodeDecl)->getParent(); @@ -398,7 +398,8 @@ static void EmitBaseInitializer(CodeGenFunction &CGF, BaseClassDecl, isBaseVirtual); - AggValueSlot AggSlot = AggValueSlot::forAddr(V, false, /*Lifetime*/ true); + AggValueSlot AggSlot = AggValueSlot::forAddr(V, Qualifiers(), + /*Lifetime*/ true); CGF.EmitAggExpr(BaseInit->getInit(), AggSlot); @@ -428,10 +429,18 @@ static void EmitAggMemberInitializer(CodeGenFunction &CGF, CGF.Builder.CreateStore(Next, ArrayIndexVar); } - AggValueSlot Slot = AggValueSlot::forAddr(Dest, LHS.isVolatileQualified(), - /*Lifetime*/ true); - - CGF.EmitAggExpr(MemberInit->getInit(), Slot); + if (!CGF.hasAggregateLLVMType(T)) { + LValue lvalue = CGF.MakeAddrLValue(Dest, T); + CGF.EmitScalarInit(MemberInit->getInit(), /*decl*/ 0, lvalue, false); + } else if (T->isAnyComplexType()) { + CGF.EmitComplexExprIntoAddr(MemberInit->getInit(), Dest, + LHS.isVolatileQualified()); + } else { + AggValueSlot Slot = AggValueSlot::forAddr(Dest, LHS.getQuals(), + /*Lifetime*/ true); + + CGF.EmitAggExpr(MemberInit->getInit(), Slot); + } return; } @@ -502,7 +511,7 @@ namespace { CallMemberDtor(FieldDecl *Field, CXXDestructorDecl *Dtor) : Field(Field), Dtor(Dtor) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { // FIXME: Is this OK for C++0x delegating constructors? llvm::Value *ThisPtr = CGF.LoadCXXThis(); LValue LHS = CGF.EmitLValueForField(ThisPtr, Field, 0); @@ -540,15 +549,15 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, // FIXME: If there's no initializer and the CXXCtorInitializer // was implicitly generated, we shouldn't be zeroing memory. - RValue RHS; - if (FieldType->isReferenceType()) { - RHS = CGF.EmitReferenceBindingToExpr(MemberInit->getInit(), Field); - CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); - } else if (FieldType->isArrayType() && !MemberInit->getInit()) { + if (FieldType->isArrayType() && !MemberInit->getInit()) { CGF.EmitNullInitialization(LHS.getAddress(), Field->getType()); } else if (!CGF.hasAggregateLLVMType(Field->getType())) { - RHS = RValue::get(CGF.EmitScalarExpr(MemberInit->getInit())); - CGF.EmitStoreThroughLValue(RHS, LHS, FieldType); + if (LHS.isSimple()) { + CGF.EmitExprAsInit(MemberInit->getInit(), Field, LHS, false); + } else { + RValue RHS = RValue::get(CGF.EmitScalarExpr(MemberInit->getInit())); + CGF.EmitStoreThroughLValue(RHS, LHS); + } } else if (MemberInit->getInit()->getType()->isAnyComplexType()) { CGF.EmitComplexExprIntoAddr(MemberInit->getInit(), LHS.getAddress(), LHS.isVolatileQualified()); @@ -576,11 +585,11 @@ static void EmitMemberInitializer(CodeGenFunction &CGF, llvm::Value *Zero = llvm::Constant::getNullValue(SizeTy); CGF.Builder.CreateStore(Zero, ArrayIndexVar); - // If we are copying an array of scalars or classes with trivial copy + // If we are copying an array of PODs or classes with trivial copy // constructors, perform a single aggregate copy. - const RecordType *Record = BaseElementTy->getAs<RecordType>(); - if (!Record || - cast<CXXRecordDecl>(Record->getDecl())->hasTrivialCopyConstructor()) { + const CXXRecordDecl *Record = BaseElementTy->getAsCXXRecordDecl(); + if (BaseElementTy.isPODType(CGF.getContext()) || + (Record && Record->hasTrivialCopyConstructor())) { // Find the source pointer. We knows it's the last argument because // we know we're in a copy constructor. unsigned SrcArgIndex = Args.size() - 1; @@ -912,7 +921,7 @@ namespace { struct CallDtorDelete : EHScopeStack::Cleanup { CallDtorDelete() {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { const CXXDestructorDecl *Dtor = cast<CXXDestructorDecl>(CGF.CurCodeDecl); const CXXRecordDecl *ClassDecl = Dtor->getParent(); CGF.EmitDeleteCall(Dtor->getOperatorDelete(), CGF.LoadCXXThis(), @@ -920,48 +929,25 @@ namespace { } }; - struct CallArrayFieldDtor : EHScopeStack::Cleanup { - const FieldDecl *Field; - CallArrayFieldDtor(const FieldDecl *Field) : Field(Field) {} - - void Emit(CodeGenFunction &CGF, bool IsForEH) { - QualType FieldType = Field->getType(); - const ConstantArrayType *Array = - CGF.getContext().getAsConstantArrayType(FieldType); + class DestroyField : public EHScopeStack::Cleanup { + const FieldDecl *field; + CodeGenFunction::Destroyer &destroyer; + bool useEHCleanupForArray; + + public: + DestroyField(const FieldDecl *field, CodeGenFunction::Destroyer *destroyer, + bool useEHCleanupForArray) + : field(field), destroyer(*destroyer), + useEHCleanupForArray(useEHCleanupForArray) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + // Find the address of the field. + llvm::Value *thisValue = CGF.LoadCXXThis(); + LValue LV = CGF.EmitLValueForField(thisValue, field, /*CVRQualifiers=*/0); + assert(LV.isSimple()); - QualType BaseType = - CGF.getContext().getBaseElementType(Array->getElementType()); - const CXXRecordDecl *FieldClassDecl = BaseType->getAsCXXRecordDecl(); - - llvm::Value *ThisPtr = CGF.LoadCXXThis(); - LValue LHS = CGF.EmitLValueForField(ThisPtr, Field, - // FIXME: Qualifiers? - /*CVRQualifiers=*/0); - - const llvm::Type *BasePtr = CGF.ConvertType(BaseType)->getPointerTo(); - llvm::Value *BaseAddrPtr = - CGF.Builder.CreateBitCast(LHS.getAddress(), BasePtr); - CGF.EmitCXXAggrDestructorCall(FieldClassDecl->getDestructor(), - Array, BaseAddrPtr); - } - }; - - struct CallFieldDtor : EHScopeStack::Cleanup { - const FieldDecl *Field; - CallFieldDtor(const FieldDecl *Field) : Field(Field) {} - - void Emit(CodeGenFunction &CGF, bool IsForEH) { - const CXXRecordDecl *FieldClassDecl = - Field->getType()->getAsCXXRecordDecl(); - - llvm::Value *ThisPtr = CGF.LoadCXXThis(); - LValue LHS = CGF.EmitLValueForField(ThisPtr, Field, - // FIXME: Qualifiers? - /*CVRQualifiers=*/0); - - CGF.EmitCXXDestructorCall(FieldClassDecl->getDestructor(), - Dtor_Complete, /*ForVirtualBase=*/false, - LHS.getAddress()); + CGF.emitDestroy(LV.getAddress(), field->getType(), destroyer, + flags.isForNormalCleanup() && useEHCleanupForArray); } }; } @@ -1035,96 +1021,103 @@ void CodeGenFunction::EnterDtorCleanups(const CXXDestructorDecl *DD, llvm::SmallVector<const FieldDecl *, 16> FieldDecls; for (CXXRecordDecl::field_iterator I = ClassDecl->field_begin(), E = ClassDecl->field_end(); I != E; ++I) { - const FieldDecl *Field = *I; - - QualType FieldType = getContext().getCanonicalType(Field->getType()); - const ConstantArrayType *Array = - getContext().getAsConstantArrayType(FieldType); - if (Array) - FieldType = getContext().getBaseElementType(Array->getElementType()); - - const RecordType *RT = FieldType->getAs<RecordType>(); - if (!RT) - continue; - - CXXRecordDecl *FieldClassDecl = cast<CXXRecordDecl>(RT->getDecl()); - if (FieldClassDecl->hasTrivialDestructor()) - continue; - - if (Array) - EHStack.pushCleanup<CallArrayFieldDtor>(NormalAndEHCleanup, Field); - else - EHStack.pushCleanup<CallFieldDtor>(NormalAndEHCleanup, Field); + const FieldDecl *field = *I; + QualType type = field->getType(); + QualType::DestructionKind dtorKind = type.isDestructedType(); + if (!dtorKind) continue; + + CleanupKind cleanupKind = getCleanupKind(dtorKind); + EHStack.pushCleanup<DestroyField>(cleanupKind, field, + getDestroyer(dtorKind), + cleanupKind & EHCleanup); } } -/// EmitCXXAggrConstructorCall - This routine essentially creates a (nested) -/// for-loop to call the default constructor on individual members of the -/// array. -/// 'D' is the default constructor for elements of the array, 'ArrayTy' is the -/// array type and 'ArrayPtr' points to the beginning fo the array. -/// It is assumed that all relevant checks have been made by the caller. +/// EmitCXXAggrConstructorCall - Emit a loop to call a particular +/// constructor for each of several members of an array. /// -/// \param ZeroInitialization True if each element should be zero-initialized -/// before it is constructed. +/// \param ctor the constructor to call for each element +/// \param argBegin,argEnd the arguments to evaluate and pass to the +/// constructor +/// \param arrayType the type of the array to initialize +/// \param arrayBegin an arrayType* +/// \param zeroInitialize true if each element should be +/// zero-initialized before it is constructed void -CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, - const ConstantArrayType *ArrayTy, - llvm::Value *ArrayPtr, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd, - bool ZeroInitialization) { - - const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); - llvm::Value * NumElements = - llvm::ConstantInt::get(SizeTy, - getContext().getConstantArrayElementCount(ArrayTy)); - - EmitCXXAggrConstructorCall(D, NumElements, ArrayPtr, ArgBeg, ArgEnd, - ZeroInitialization); +CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, + const ConstantArrayType *arrayType, + llvm::Value *arrayBegin, + CallExpr::const_arg_iterator argBegin, + CallExpr::const_arg_iterator argEnd, + bool zeroInitialize) { + QualType elementType; + llvm::Value *numElements = + emitArrayLength(arrayType, elementType, arrayBegin); + + EmitCXXAggrConstructorCall(ctor, numElements, arrayBegin, + argBegin, argEnd, zeroInitialize); } +/// EmitCXXAggrConstructorCall - Emit a loop to call a particular +/// constructor for each of several members of an array. +/// +/// \param ctor the constructor to call for each element +/// \param numElements the number of elements in the array; +/// may be zero +/// \param argBegin,argEnd the arguments to evaluate and pass to the +/// constructor +/// \param arrayBegin a T*, where T is the type constructed by ctor +/// \param zeroInitialize true if each element should be +/// zero-initialized before it is constructed void -CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, - llvm::Value *NumElements, - llvm::Value *ArrayPtr, - CallExpr::const_arg_iterator ArgBeg, - CallExpr::const_arg_iterator ArgEnd, - bool ZeroInitialization) { - const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); - - // Create a temporary for the loop index and initialize it with 0. - llvm::Value *IndexPtr = CreateTempAlloca(SizeTy, "loop.index"); - llvm::Value *Zero = llvm::Constant::getNullValue(SizeTy); - Builder.CreateStore(Zero, IndexPtr); - - // Start the loop with a block that tests the condition. - llvm::BasicBlock *CondBlock = createBasicBlock("for.cond"); - llvm::BasicBlock *AfterFor = createBasicBlock("for.end"); - - EmitBlock(CondBlock); - - llvm::BasicBlock *ForBody = createBasicBlock("for.body"); - - // Generate: if (loop-index < number-of-elements fall to the loop body, - // otherwise, go to the block after the for-loop. - llvm::Value *Counter = Builder.CreateLoad(IndexPtr); - llvm::Value *IsLess = Builder.CreateICmpULT(Counter, NumElements, "isless"); - // If the condition is true, execute the body. - Builder.CreateCondBr(IsLess, ForBody, AfterFor); - - EmitBlock(ForBody); +CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *ctor, + llvm::Value *numElements, + llvm::Value *arrayBegin, + CallExpr::const_arg_iterator argBegin, + CallExpr::const_arg_iterator argEnd, + bool zeroInitialize) { + + // It's legal for numElements to be zero. This can happen both + // dynamically, because x can be zero in 'new A[x]', and statically, + // because of GCC extensions that permit zero-length arrays. There + // are probably legitimate places where we could assume that this + // doesn't happen, but it's not clear that it's worth it. + llvm::BranchInst *zeroCheckBranch = 0; + + // Optimize for a constant count. + llvm::ConstantInt *constantCount + = dyn_cast<llvm::ConstantInt>(numElements); + if (constantCount) { + // Just skip out if the constant count is zero. + if (constantCount->isZero()) return; + + // Otherwise, emit the check. + } else { + llvm::BasicBlock *loopBB = createBasicBlock("new.ctorloop"); + llvm::Value *iszero = Builder.CreateIsNull(numElements, "isempty"); + zeroCheckBranch = Builder.CreateCondBr(iszero, loopBB, loopBB); + EmitBlock(loopBB); + } + + // Find the end of the array. + llvm::Value *arrayEnd = Builder.CreateInBoundsGEP(arrayBegin, numElements, + "arrayctor.end"); + + // Enter the loop, setting up a phi for the current location to initialize. + llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); + llvm::BasicBlock *loopBB = createBasicBlock("arrayctor.loop"); + EmitBlock(loopBB); + llvm::PHINode *cur = Builder.CreatePHI(arrayBegin->getType(), 2, + "arrayctor.cur"); + cur->addIncoming(arrayBegin, entryBB); - llvm::BasicBlock *ContinueBlock = createBasicBlock("for.inc"); // Inside the loop body, emit the constructor call on the array element. - Counter = Builder.CreateLoad(IndexPtr); - llvm::Value *Address = Builder.CreateInBoundsGEP(ArrayPtr, Counter, - "arrayidx"); + + QualType type = getContext().getTypeDeclType(ctor->getParent()); // Zero initialize the storage, if requested. - if (ZeroInitialization) - EmitNullInitialization(Address, - getContext().getTypeDeclType(D->getParent())); + if (zeroInitialize) + EmitNullInitialization(cur, type); // C++ [class.temporary]p4: // There are two contexts in which temporaries are destroyed at a different @@ -1134,99 +1127,47 @@ CodeGenFunction::EmitCXXAggrConstructorCall(const CXXConstructorDecl *D, // every temporary created in a default argument expression is sequenced // before the construction of the next array element, if any. - // Keep track of the current number of live temporaries. { RunCleanupsScope Scope(*this); - EmitCXXConstructorCall(D, Ctor_Complete, /*ForVirtualBase=*/false, Address, - ArgBeg, ArgEnd); + // Evaluate the constructor and its arguments in a regular + // partial-destroy cleanup. + if (getLangOptions().Exceptions && + !ctor->getParent()->hasTrivialDestructor()) { + Destroyer *destroyer = destroyCXXObject; + pushRegularPartialArrayCleanup(arrayBegin, cur, type, *destroyer); + } + + EmitCXXConstructorCall(ctor, Ctor_Complete, /*ForVirtualBase=*/ false, + cur, argBegin, argEnd); } - EmitBlock(ContinueBlock); + // Go to the next element. + llvm::Value *next = + Builder.CreateInBoundsGEP(cur, llvm::ConstantInt::get(SizeTy, 1), + "arrayctor.next"); + cur->addIncoming(next, Builder.GetInsertBlock()); - // Emit the increment of the loop counter. - llvm::Value *NextVal = llvm::ConstantInt::get(SizeTy, 1); - Counter = Builder.CreateLoad(IndexPtr); - NextVal = Builder.CreateAdd(Counter, NextVal, "inc"); - Builder.CreateStore(NextVal, IndexPtr); + // Check whether that's the end of the loop. + llvm::Value *done = Builder.CreateICmpEQ(next, arrayEnd, "arrayctor.done"); + llvm::BasicBlock *contBB = createBasicBlock("arrayctor.cont"); + Builder.CreateCondBr(done, contBB, loopBB); - // Finally, branch back up to the condition for the next iteration. - EmitBranch(CondBlock); + // Patch the earlier check to skip over the loop. + if (zeroCheckBranch) zeroCheckBranch->setSuccessor(0, contBB); - // Emit the fall-through block. - EmitBlock(AfterFor, true); -} - -/// EmitCXXAggrDestructorCall - calls the default destructor on array -/// elements in reverse order of construction. -void -CodeGenFunction::EmitCXXAggrDestructorCall(const CXXDestructorDecl *D, - const ArrayType *Array, - llvm::Value *This) { - const ConstantArrayType *CA = dyn_cast<ConstantArrayType>(Array); - assert(CA && "Do we support VLA for destruction ?"); - uint64_t ElementCount = getContext().getConstantArrayElementCount(CA); - - const llvm::Type *SizeLTy = ConvertType(getContext().getSizeType()); - llvm::Value* ElementCountPtr = llvm::ConstantInt::get(SizeLTy, ElementCount); - EmitCXXAggrDestructorCall(D, ElementCountPtr, This); + EmitBlock(contBB); } -/// EmitCXXAggrDestructorCall - calls the default destructor on array -/// elements in reverse order of construction. -void -CodeGenFunction::EmitCXXAggrDestructorCall(const CXXDestructorDecl *D, - llvm::Value *UpperCount, - llvm::Value *This) { - const llvm::Type *SizeLTy = ConvertType(getContext().getSizeType()); - llvm::Value *One = llvm::ConstantInt::get(SizeLTy, 1); - - // Create a temporary for the loop index and initialize it with count of - // array elements. - llvm::Value *IndexPtr = CreateTempAlloca(SizeLTy, "loop.index"); - - // Store the number of elements in the index pointer. - Builder.CreateStore(UpperCount, IndexPtr); - - // Start the loop with a block that tests the condition. - llvm::BasicBlock *CondBlock = createBasicBlock("for.cond"); - llvm::BasicBlock *AfterFor = createBasicBlock("for.end"); - - EmitBlock(CondBlock); - - llvm::BasicBlock *ForBody = createBasicBlock("for.body"); - - // Generate: if (loop-index != 0 fall to the loop body, - // otherwise, go to the block after the for-loop. - llvm::Value* zeroConstant = - llvm::Constant::getNullValue(SizeLTy); - llvm::Value *Counter = Builder.CreateLoad(IndexPtr); - llvm::Value *IsNE = Builder.CreateICmpNE(Counter, zeroConstant, - "isne"); - // If the condition is true, execute the body. - Builder.CreateCondBr(IsNE, ForBody, AfterFor); - - EmitBlock(ForBody); - - llvm::BasicBlock *ContinueBlock = createBasicBlock("for.inc"); - // Inside the loop body, emit the constructor call on the array element. - Counter = Builder.CreateLoad(IndexPtr); - Counter = Builder.CreateSub(Counter, One); - llvm::Value *Address = Builder.CreateInBoundsGEP(This, Counter, "arrayidx"); - EmitCXXDestructorCall(D, Dtor_Complete, /*ForVirtualBase=*/false, Address); - - EmitBlock(ContinueBlock); - - // Emit the decrement of the loop counter. - Counter = Builder.CreateLoad(IndexPtr); - Counter = Builder.CreateSub(Counter, One, "dec"); - Builder.CreateStore(Counter, IndexPtr); - - // Finally, branch back up to the condition for the next iteration. - EmitBranch(CondBlock); - - // Emit the fall-through block. - EmitBlock(AfterFor, true); +void CodeGenFunction::destroyCXXObject(CodeGenFunction &CGF, + llvm::Value *addr, + QualType type) { + const RecordType *rtype = type->castAs<RecordType>(); + const CXXRecordDecl *record = cast<CXXRecordDecl>(rtype->getDecl()); + const CXXDestructorDecl *dtor = record->getDestructor(); + assert(!dtor->isTrivial()); + CGF.EmitCXXDestructorCall(dtor, Dtor_Complete, /*for vbase*/ false, + addr); } void @@ -1370,7 +1311,7 @@ namespace { CXXDtorType Type) : Dtor(D), Addr(Addr), Type(Type) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.EmitCXXDestructorCall(Dtor, Type, /*ForVirtualBase=*/false, Addr); } @@ -1384,7 +1325,8 @@ CodeGenFunction::EmitDelegatingCXXConstructorCall(const CXXConstructorDecl *Ctor llvm::Value *ThisPtr = LoadCXXThis(); - AggValueSlot AggSlot = AggValueSlot::forAddr(ThisPtr, false, /*Lifetime*/ true); + AggValueSlot AggSlot = + AggValueSlot::forAddr(ThisPtr, Qualifiers(), /*Lifetime*/ true); EmitAggExpr(Ctor->init_begin()[0]->getInit(), AggSlot); @@ -1424,7 +1366,7 @@ namespace { CallLocalDtor(const CXXDestructorDecl *D, llvm::Value *Addr) : Dtor(D), Addr(Addr) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, Addr); } diff --git a/lib/CodeGen/CGCleanup.cpp b/lib/CodeGen/CGCleanup.cpp index 41ecd8111790..9c5dd1f23721 100644 --- a/lib/CodeGen/CGCleanup.cpp +++ b/lib/CodeGen/CGCleanup.cpp @@ -49,8 +49,7 @@ DominatingValue<RValue>::saved_type::save(CodeGenFunction &CGF, RValue rv) { if (rv.isComplex()) { CodeGenFunction::ComplexPairTy V = rv.getComplexVal(); const llvm::Type *ComplexTy = - llvm::StructType::get(CGF.getLLVMContext(), - V.first->getType(), V.second->getType(), + llvm::StructType::get(V.first->getType(), V.second->getType(), (void*) 0); llvm::Value *addr = CGF.CreateTempAlloca(ComplexTy, "saved-complex"); CGF.StoreComplexToAddr(V, addr, /*volatile*/ false); @@ -257,9 +256,7 @@ void CodeGenFunction::initFullExprCleanup() { if (cleanup.isEHCleanup()) cleanup.setTestFlagInEHCleanup(); } -EHScopeStack::Cleanup::~Cleanup() { - llvm_unreachable("Cleanup is indestructable"); -} +void EHScopeStack::Cleanup::anchor() {} /// All the branch fixups on the EH stack have propagated out past the /// outermost normal cleanup; resolve them all by adding cases to the @@ -421,13 +418,13 @@ static llvm::BasicBlock *SimplifyCleanupEntry(CodeGenFunction &CGF, // Kill the branch. Br->eraseFromParent(); - // Merge the blocks. - Pred->getInstList().splice(Pred->end(), Entry->getInstList()); - // Replace all uses of the entry with the predecessor, in case there // are phis in the cleanup. Entry->replaceAllUsesWith(Pred); + // Merge the blocks. + Pred->getInstList().splice(Pred->end(), Entry->getInstList()); + // Kill the entry block. Entry->eraseFromParent(); @@ -439,10 +436,10 @@ static llvm::BasicBlock *SimplifyCleanupEntry(CodeGenFunction &CGF, static void EmitCleanup(CodeGenFunction &CGF, EHScopeStack::Cleanup *Fn, - bool ForEH, + EHScopeStack::Cleanup::Flags flags, llvm::Value *ActiveFlag) { // EH cleanups always occur within a terminate scope. - if (ForEH) CGF.EHStack.pushTerminate(); + if (flags.isForEHCleanup()) CGF.EHStack.pushTerminate(); // If there's an active flag, load it and skip the cleanup if it's // false. @@ -457,7 +454,7 @@ static void EmitCleanup(CodeGenFunction &CGF, } // Ask the cleanup to emit itself. - Fn->Emit(CGF, ForEH); + Fn->Emit(CGF, flags); assert(CGF.HaveInsertPoint() && "cleanup ended with no insertion point?"); // Emit the continuation block if there was an active flag. @@ -465,7 +462,7 @@ static void EmitCleanup(CodeGenFunction &CGF, CGF.EmitBlock(ContBB); // Leave the terminate scope. - if (ForEH) CGF.EHStack.popTerminate(); + if (flags.isForEHCleanup()) CGF.EHStack.popTerminate(); } static void ForwardPrebranchedFallthrough(llvm::BasicBlock *Exit, @@ -540,6 +537,12 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { RequiresNormalCleanup = true; } + EHScopeStack::Cleanup::Flags cleanupFlags; + if (Scope.isNormalCleanup()) + cleanupFlags.setIsNormalCleanupKind(); + if (Scope.isEHCleanup()) + cleanupFlags.setIsEHCleanupKind(); + // Even if we don't need the normal cleanup, we might still have // prebranched fallthrough to worry about. if (Scope.isNormalCleanup() && !RequiresNormalCleanup && @@ -663,7 +666,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { EHStack.popCleanup(); - EmitCleanup(*this, Fn, /*ForEH*/ false, NormalActiveFlag); + EmitCleanup(*this, Fn, cleanupFlags, NormalActiveFlag); // Otherwise, the best approach is to thread everything through // the cleanup block and then try to clean up after ourselves. @@ -774,7 +777,7 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { EHStack.popCleanup(); assert(EHStack.hasNormalCleanups() == HasEnclosingCleanups); - EmitCleanup(*this, Fn, /*ForEH*/ false, NormalActiveFlag); + EmitCleanup(*this, Fn, cleanupFlags, NormalActiveFlag); // Append the prepared cleanup prologue from above. llvm::BasicBlock *NormalExit = Builder.GetInsertBlock(); @@ -857,7 +860,9 @@ void CodeGenFunction::PopCleanupBlock(bool FallthroughIsBranchThrough) { CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); EmitBlock(EHEntry); - EmitCleanup(*this, Fn, /*ForEH*/ true, EHActiveFlag); + + cleanupFlags.setIsForEHCleanup(); + EmitCleanup(*this, Fn, cleanupFlags, EHActiveFlag); // Append the prepared cleanup prologue from above. llvm::BasicBlock *EHExit = Builder.GetInsertBlock(); diff --git a/lib/CodeGen/CGDebugInfo.cpp b/lib/CodeGen/CGDebugInfo.cpp index 98d30db647d5..4c1244591743 100644 --- a/lib/CodeGen/CGDebugInfo.cpp +++ b/lib/CodeGen/CGDebugInfo.cpp @@ -314,8 +314,9 @@ llvm::DIType CGDebugInfo::CreateType(const BuiltinType *BT) { llvm::SmallVector<llvm::Value *, 16> EltTys; llvm::DIType FieldTy = - DBuilder.createMemberType("isa", getOrCreateMainFile(), - 0,Size, 0, 0, 0, ISATy); + DBuilder.createMemberType(getOrCreateMainFile(), "isa", + getOrCreateMainFile(), 0, Size, + 0, 0, 0, ISATy); EltTys.push_back(FieldTy); llvm::DIArray Elements = DBuilder.getOrCreateArray(EltTys); @@ -389,6 +390,7 @@ llvm::DIType CGDebugInfo::CreateQualifiedType(QualType Ty, llvm::DIFile Unit) { // Ignore these qualifiers for now. Qc.removeObjCGCAttr(); Qc.removeAddressSpace(); + Qc.removeObjCLifetime(); // We will create one Derived type for one qualifier and recurse to handle any // additional ones. @@ -528,7 +530,7 @@ llvm::DIType CGDebugInfo::CreateType(const BlockPointerType *Ty, FieldTy = DescTy; FieldSize = CGM.getContext().getTypeSize(Ty); FieldAlign = CGM.getContext().getTypeAlign(Ty); - FieldTy = DBuilder.createMemberType("__descriptor", Unit, + FieldTy = DBuilder.createMemberType(Unit, "__descriptor", Unit, LineNo, FieldSize, FieldAlign, FieldOffset, 0, FieldTy); EltTys.push_back(FieldTy); @@ -591,7 +593,8 @@ llvm::DIType CGDebugInfo::createFieldType(llvm::StringRef name, SourceLocation loc, AccessSpecifier AS, uint64_t offsetInBits, - llvm::DIFile tunit) { + llvm::DIFile tunit, + llvm::DIDescriptor scope) { llvm::DIType debugType = getOrCreateType(type, tunit); // Get the location for the field. @@ -613,15 +616,16 @@ llvm::DIType CGDebugInfo::createFieldType(llvm::StringRef name, else if (AS == clang::AS_protected) flags |= llvm::DIDescriptor::FlagProtected; - return DBuilder.createMemberType(name, file, line, sizeInBits, alignInBits, - offsetInBits, flags, debugType); + return DBuilder.createMemberType(scope, name, file, line, sizeInBits, + alignInBits, offsetInBits, flags, debugType); } /// CollectRecordFields - A helper function to collect debug info for /// record fields. This is used while creating debug info entry for a Record. void CGDebugInfo:: CollectRecordFields(const RecordDecl *record, llvm::DIFile tunit, - llvm::SmallVectorImpl<llvm::Value *> &elements) { + llvm::SmallVectorImpl<llvm::Value *> &elements, + llvm::DIType RecordTy) { unsigned fieldNo = 0; const FieldDecl *LastFD = 0; bool IsMsStruct = record->hasAttr<MsStructAttr>(); @@ -652,7 +656,7 @@ CollectRecordFields(const RecordDecl *record, llvm::DIFile tunit, llvm::DIType fieldType = createFieldType(name, type, field->getBitWidth(), field->getLocation(), field->getAccess(), - layout.getFieldOffset(fieldNo), tunit); + layout.getFieldOffset(fieldNo), tunit, RecordTy); elements.push_back(fieldType); } @@ -960,7 +964,7 @@ CollectVTableInfo(const CXXRecordDecl *RD, llvm::DIFile Unit, unsigned Size = CGM.getContext().getTypeSize(CGM.getContext().VoidPtrTy); llvm::DIType VPTR - = DBuilder.createMemberType(getVTableName(RD), Unit, + = DBuilder.createMemberType(Unit, getVTableName(RD), Unit, 0, Size, 0, 0, 0, getOrCreateVTablePtrType(Unit)); EltTys.push_back(VPTR); @@ -1048,7 +1052,7 @@ llvm::DIType CGDebugInfo::CreateType(const RecordType *Ty) { } } - CollectRecordFields(RD, Unit, EltTys); + CollectRecordFields(RD, Unit, EltTys, FwdDecl); llvm::DIArray TParamsArray; if (CXXDecl) { CollectCXXMemberFunctions(CXXDecl, Unit, EltTys, FwdDecl); @@ -1379,13 +1383,13 @@ llvm::DIType CGDebugInfo::CreateType(const MemberPointerType *Ty, // FIXME: This should probably be a function type instead. ElementTypes[0] = - DBuilder.createMemberType("ptr", U, 0, + DBuilder.createMemberType(U, "ptr", U, 0, Info.first, Info.second, FieldOffset, 0, PointerDiffDITy); FieldOffset += Info.first; ElementTypes[1] = - DBuilder.createMemberType("ptr", U, 0, + DBuilder.createMemberType(U, "ptr", U, 0, Info.first, Info.second, FieldOffset, 0, PointerDiffDITy); @@ -1520,10 +1524,7 @@ llvm::DIType CGDebugInfo::CreateTypeNode(QualType Ty, #include "clang/AST/TypeNodes.def" assert(false && "Dependent types cannot show up in debug information"); - // FIXME: Handle these. case Type::ExtVector: - return llvm::DIType(); - case Type::Vector: return CreateType(cast<VectorType>(Ty), Unit); case Type::ObjCObjectPointer: @@ -1586,7 +1587,7 @@ llvm::DIType CGDebugInfo::CreateMemberType(llvm::DIFile Unit, QualType FType, llvm::DIType FieldTy = CGDebugInfo::getOrCreateType(FType, Unit); uint64_t FieldSize = CGM.getContext().getTypeSize(FType); unsigned FieldAlign = CGM.getContext().getTypeAlign(FType); - llvm::DIType Ty = DBuilder.createMemberType(Name, Unit, 0, + llvm::DIType Ty = DBuilder.createMemberType(Unit, Name, Unit, 0, FieldSize, FieldAlign, *Offset, 0, FieldTy); *Offset += FieldSize; @@ -1897,7 +1898,7 @@ llvm::DIType CGDebugInfo::EmitTypeForVarWithBlocksAttr(const ValueDecl *VD, FieldAlign = CGM.getContext().toBits(Align); *XOffset = FieldOffset; - FieldTy = DBuilder.createMemberType(VD->getName(), Unit, + FieldTy = DBuilder.createMemberType(Unit, VD->getName(), Unit, 0, FieldSize, FieldAlign, FieldOffset, 0, FieldTy); EltTys.push_back(FieldTy); @@ -2133,23 +2134,23 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, llvm::SmallVector<llvm::Value*, 16> fields; fields.push_back(createFieldType("__isa", C.VoidPtrTy, 0, loc, AS_public, blockLayout->getElementOffsetInBits(0), - tunit)); + tunit, tunit)); fields.push_back(createFieldType("__flags", C.IntTy, 0, loc, AS_public, blockLayout->getElementOffsetInBits(1), - tunit)); + tunit, tunit)); fields.push_back(createFieldType("__reserved", C.IntTy, 0, loc, AS_public, blockLayout->getElementOffsetInBits(2), - tunit)); + tunit, tunit)); fields.push_back(createFieldType("__FuncPtr", C.VoidPtrTy, 0, loc, AS_public, blockLayout->getElementOffsetInBits(3), - tunit)); + tunit, tunit)); fields.push_back(createFieldType("__descriptor", C.getPointerType(block.NeedsCopyDispose ? C.getBlockDescriptorExtendedType() : C.getBlockDescriptorType()), 0, loc, AS_public, blockLayout->getElementOffsetInBits(4), - tunit)); + tunit, tunit)); // We want to sort the captures by offset, not because DWARF // requires this, but because we're paranoid about debuggers. @@ -2198,7 +2199,7 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, QualType type = method->getThisType(C); fields.push_back(createFieldType("this", type, 0, loc, AS_public, - offsetInBits, tunit)); + offsetInBits, tunit, tunit)); continue; } @@ -2213,12 +2214,12 @@ void CGDebugInfo::EmitDeclareOfBlockLiteralArgVariable(const CGBlockInfo &block, uint64_t xoffset; fieldType = EmitTypeForVarWithBlocksAttr(variable, &xoffset); fieldType = DBuilder.createPointerType(fieldType, ptrInfo.first); - fieldType = DBuilder.createMemberType(name, tunit, line, + fieldType = DBuilder.createMemberType(tunit, name, tunit, line, ptrInfo.first, ptrInfo.second, offsetInBits, 0, fieldType); } else { fieldType = createFieldType(name, variable->getType(), 0, - loc, AS_public, offsetInBits, tunit); + loc, AS_public, offsetInBits, tunit, tunit); } fields.push_back(fieldType); } diff --git a/lib/CodeGen/CGDebugInfo.h b/lib/CodeGen/CGDebugInfo.h index 6ec6b65f5e01..f87d0072e323 100644 --- a/lib/CodeGen/CGDebugInfo.h +++ b/lib/CodeGen/CGDebugInfo.h @@ -33,11 +33,11 @@ namespace clang { class VarDecl; class ObjCInterfaceDecl; class ClassTemplateSpecializationDecl; + class GlobalDecl; namespace CodeGen { class CodeGenModule; class CodeGenFunction; - class GlobalDecl; class CGBlockInfo; /// CGDebugInfo - This class gathers all debug information during compilation @@ -139,9 +139,11 @@ class CGDebugInfo { llvm::DIType createFieldType(llvm::StringRef name, QualType type, Expr *bitWidth, SourceLocation loc, AccessSpecifier AS, uint64_t offsetInBits, - llvm::DIFile tunit); + llvm::DIFile tunit, + llvm::DIDescriptor scope); void CollectRecordFields(const RecordDecl *Decl, llvm::DIFile F, - llvm::SmallVectorImpl<llvm::Value *> &E); + llvm::SmallVectorImpl<llvm::Value *> &E, + llvm::DIType RecordTy); void CollectVTableInfo(const CXXRecordDecl *Decl, llvm::DIFile F, diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp index 8a1a8536dab1..62c3a9791d0f 100644 --- a/lib/CodeGen/CGDecl.cpp +++ b/lib/CodeGen/CGDecl.cpp @@ -98,7 +98,7 @@ void CodeGenFunction::EmitDecl(const Decl &D) { QualType Ty = TD.getUnderlyingType(); if (Ty->isVariablyModifiedType()) - EmitVLASize(Ty); + EmitVariablyModifiedType(Ty); } } } @@ -258,7 +258,7 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, // even though that doesn't really make any sense. // Make sure to evaluate VLA bounds now so that we have them for later. if (D.getType()->isVariablyModifiedType()) - EmitVLASize(D.getType()); + EmitVariablyModifiedType(D.getType()); // Local static block variables must be treated as globals as they may be // referenced in their RHS initializer block-literal expresion. @@ -304,38 +304,40 @@ void CodeGenFunction::EmitStaticVarDecl(const VarDecl &D, } namespace { - struct CallArrayDtor : EHScopeStack::Cleanup { - CallArrayDtor(const CXXDestructorDecl *Dtor, - const ConstantArrayType *Type, - llvm::Value *Loc) - : Dtor(Dtor), Type(Type), Loc(Loc) {} - - const CXXDestructorDecl *Dtor; - const ConstantArrayType *Type; - llvm::Value *Loc; - - void Emit(CodeGenFunction &CGF, bool IsForEH) { - QualType BaseElementTy = CGF.getContext().getBaseElementType(Type); - const llvm::Type *BasePtr = CGF.ConvertType(BaseElementTy); - BasePtr = llvm::PointerType::getUnqual(BasePtr); - llvm::Value *BaseAddrPtr = CGF.Builder.CreateBitCast(Loc, BasePtr); - CGF.EmitCXXAggrDestructorCall(Dtor, Type, BaseAddrPtr); + struct DestroyObject : EHScopeStack::Cleanup { + DestroyObject(llvm::Value *addr, QualType type, + CodeGenFunction::Destroyer *destroyer, + bool useEHCleanupForArray) + : addr(addr), type(type), destroyer(*destroyer), + useEHCleanupForArray(useEHCleanupForArray) {} + + llvm::Value *addr; + QualType type; + CodeGenFunction::Destroyer &destroyer; + bool useEHCleanupForArray; + + void Emit(CodeGenFunction &CGF, Flags flags) { + // Don't use an EH cleanup recursively from an EH cleanup. + bool useEHCleanupForArray = + flags.isForNormalCleanup() && this->useEHCleanupForArray; + + CGF.emitDestroy(addr, type, destroyer, useEHCleanupForArray); } }; - struct CallVarDtor : EHScopeStack::Cleanup { - CallVarDtor(const CXXDestructorDecl *Dtor, - llvm::Value *NRVOFlag, - llvm::Value *Loc) - : Dtor(Dtor), NRVOFlag(NRVOFlag), Loc(Loc) {} + struct DestroyNRVOVariable : EHScopeStack::Cleanup { + DestroyNRVOVariable(llvm::Value *addr, + const CXXDestructorDecl *Dtor, + llvm::Value *NRVOFlag) + : Dtor(Dtor), NRVOFlag(NRVOFlag), Loc(addr) {} const CXXDestructorDecl *Dtor; llvm::Value *NRVOFlag; llvm::Value *Loc; - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { // Along the exceptions path we always execute the dtor. - bool NRVO = !IsForEH && NRVOFlag; + bool NRVO = flags.isForNormalCleanup() && NRVOFlag; llvm::BasicBlock *SkipDtorBB = 0; if (NRVO) { @@ -353,19 +355,31 @@ namespace { if (NRVO) CGF.EmitBlock(SkipDtorBB); } }; -} -namespace { struct CallStackRestore : EHScopeStack::Cleanup { llvm::Value *Stack; CallStackRestore(llvm::Value *Stack) : Stack(Stack) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { llvm::Value *V = CGF.Builder.CreateLoad(Stack, "tmp"); llvm::Value *F = CGF.CGM.getIntrinsic(llvm::Intrinsic::stackrestore); CGF.Builder.CreateCall(F, V); } }; + struct ExtendGCLifetime : EHScopeStack::Cleanup { + const VarDecl &Var; + ExtendGCLifetime(const VarDecl *var) : Var(*var) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + // Compute the address of the local variable, in case it's a + // byref or something. + DeclRefExpr DRE(const_cast<VarDecl*>(&Var), Var.getType(), VK_LValue, + SourceLocation()); + llvm::Value *value = CGF.EmitLoadOfScalar(CGF.EmitDeclRefLValue(&DRE)); + CGF.EmitExtendGCLifetime(value); + } + }; + struct CallCleanupFunction : EHScopeStack::Cleanup { llvm::Constant *CleanupFn; const CGFunctionInfo &FnInfo; @@ -375,7 +389,7 @@ namespace { const VarDecl *Var) : CleanupFn(CleanupFn), FnInfo(*Info), Var(*Var) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { DeclRefExpr DRE(const_cast<VarDecl*>(&Var), Var.getType(), VK_LValue, SourceLocation()); // Compute the address of the local variable, in case it's a byref @@ -400,6 +414,207 @@ namespace { }; } +/// EmitAutoVarWithLifetime - Does the setup required for an automatic +/// variable with lifetime. +static void EmitAutoVarWithLifetime(CodeGenFunction &CGF, const VarDecl &var, + llvm::Value *addr, + Qualifiers::ObjCLifetime lifetime) { + switch (lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing to do + break; + + case Qualifiers::OCL_Strong: { + CodeGenFunction::Destroyer &destroyer = + (var.hasAttr<ObjCPreciseLifetimeAttr>() + ? CodeGenFunction::destroyARCStrongPrecise + : CodeGenFunction::destroyARCStrongImprecise); + + CleanupKind cleanupKind = CGF.getARCCleanupKind(); + CGF.pushDestroy(cleanupKind, addr, var.getType(), destroyer, + cleanupKind & EHCleanup); + break; + } + case Qualifiers::OCL_Autoreleasing: + // nothing to do + break; + + case Qualifiers::OCL_Weak: + // __weak objects always get EH cleanups; otherwise, exceptions + // could cause really nasty crashes instead of mere leaks. + CGF.pushDestroy(NormalAndEHCleanup, addr, var.getType(), + CodeGenFunction::destroyARCWeak, + /*useEHCleanup*/ true); + break; + } +} + +static bool isAccessedBy(const VarDecl &var, const Stmt *s) { + if (const Expr *e = dyn_cast<Expr>(s)) { + // Skip the most common kinds of expressions that make + // hierarchy-walking expensive. + s = e = e->IgnoreParenCasts(); + + if (const DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) + return (ref->getDecl() == &var); + } + + for (Stmt::const_child_range children = s->children(); children; ++children) + // children might be null; as in missing decl or conditional of an if-stmt. + if ((*children) && isAccessedBy(var, *children)) + return true; + + return false; +} + +static bool isAccessedBy(const ValueDecl *decl, const Expr *e) { + if (!decl) return false; + if (!isa<VarDecl>(decl)) return false; + const VarDecl *var = cast<VarDecl>(decl); + return isAccessedBy(*var, e); +} + +static void drillIntoBlockVariable(CodeGenFunction &CGF, + LValue &lvalue, + const VarDecl *var) { + lvalue.setAddress(CGF.BuildBlockByrefAddress(lvalue.getAddress(), var)); +} + +void CodeGenFunction::EmitScalarInit(const Expr *init, + const ValueDecl *D, + LValue lvalue, + bool capturedByInit) { + Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime(); + if (!lifetime) { + llvm::Value *value = EmitScalarExpr(init); + if (capturedByInit) + drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); + EmitStoreThroughLValue(RValue::get(value), lvalue); + return; + } + + // If we're emitting a value with lifetime, we have to do the + // initialization *before* we leave the cleanup scopes. + CodeGenFunction::RunCleanupsScope Scope(*this); + if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(init)) + init = ewc->getSubExpr(); + + // We have to maintain the illusion that the variable is + // zero-initialized. If the variable might be accessed in its + // initializer, zero-initialize before running the initializer, then + // actually perform the initialization with an assign. + bool accessedByInit = false; + if (lifetime != Qualifiers::OCL_ExplicitNone) + accessedByInit = isAccessedBy(D, init); + if (accessedByInit) { + LValue tempLV = lvalue; + // Drill down to the __block object if necessary. + if (capturedByInit) { + // We can use a simple GEP for this because it can't have been + // moved yet. + tempLV.setAddress(Builder.CreateStructGEP(tempLV.getAddress(), + getByRefValueLLVMField(cast<VarDecl>(D)))); + } + + const llvm::PointerType *ty + = cast<llvm::PointerType>(tempLV.getAddress()->getType()); + ty = cast<llvm::PointerType>(ty->getElementType()); + + llvm::Value *zero = llvm::ConstantPointerNull::get(ty); + + // If __weak, we want to use a barrier under certain conditions. + if (lifetime == Qualifiers::OCL_Weak) + EmitARCInitWeak(tempLV.getAddress(), zero); + + // Otherwise just do a simple store. + else + EmitStoreOfScalar(zero, tempLV); + } + + // Emit the initializer. + llvm::Value *value = 0; + + switch (lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing to do + value = EmitScalarExpr(init); + break; + + case Qualifiers::OCL_Strong: { + value = EmitARCRetainScalarExpr(init); + break; + } + + case Qualifiers::OCL_Weak: { + // No way to optimize a producing initializer into this. It's not + // worth optimizing for, because the value will immediately + // disappear in the common case. + value = EmitScalarExpr(init); + + if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); + if (accessedByInit) + EmitARCStoreWeak(lvalue.getAddress(), value, /*ignored*/ true); + else + EmitARCInitWeak(lvalue.getAddress(), value); + return; + } + + case Qualifiers::OCL_Autoreleasing: + value = EmitARCRetainAutoreleaseScalarExpr(init); + break; + } + + if (capturedByInit) drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); + + // If the variable might have been accessed by its initializer, we + // might have to initialize with a barrier. We have to do this for + // both __weak and __strong, but __weak got filtered out above. + if (accessedByInit && lifetime == Qualifiers::OCL_Strong) { + llvm::Value *oldValue = EmitLoadOfScalar(lvalue); + EmitStoreOfScalar(value, lvalue); + EmitARCRelease(oldValue, /*precise*/ false); + return; + } + + EmitStoreOfScalar(value, lvalue); +} + +/// EmitScalarInit - Initialize the given lvalue with the given object. +void CodeGenFunction::EmitScalarInit(llvm::Value *init, LValue lvalue) { + Qualifiers::ObjCLifetime lifetime = lvalue.getObjCLifetime(); + if (!lifetime) + return EmitStoreThroughLValue(RValue::get(init), lvalue); + + switch (lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing to do + break; + + case Qualifiers::OCL_Strong: + init = EmitARCRetain(lvalue.getType(), init); + break; + + case Qualifiers::OCL_Weak: + // Initialize and then skip the primitive store. + EmitARCInitWeak(lvalue.getAddress(), init); + return; + + case Qualifiers::OCL_Autoreleasing: + init = EmitARCRetainAutorelease(lvalue.getType(), init); + break; + } + + EmitStoreOfScalar(init, lvalue); +} /// canEmitInitWithFewStoresAfterMemset - Decide whether we can emit the /// non-zero parts of the specified initializer with equal or fewer than @@ -508,6 +723,10 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { CharUnits alignment = getContext().getDeclAlign(&D); emission.Alignment = alignment; + // If the type is variably-modified, emit all the VLA sizes for it. + if (Ty->isVariablyModifiedType()) + EmitVariablyModifiedType(Ty); + llvm::Value *DeclPtr; if (Ty->isConstantSizeType()) { if (!Target.useGlobalsForAutomaticVariables()) { @@ -521,7 +740,9 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { // arrays as long as the initialization is trivial (e.g. if they // have a non-trivial destructor, but not a non-trivial constructor). if (D.getInit() && - (Ty->isArrayType() || Ty->isRecordType()) && Ty->isPODType() && + (Ty->isArrayType() || Ty->isRecordType()) && + (Ty.isPODType(getContext()) || + getContext().getBaseElementType(Ty)->isObjCObjectPointerType()) && D.getInit()->isConstantInitializer(getContext(), false)) { // If the variable's a const type, and it's neither an NRVO @@ -585,10 +806,6 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { DeclPtr = CreateStaticVarDecl(D, Class, llvm::GlobalValue::InternalLinkage); } - - // FIXME: Can this happen? - if (Ty->isVariablyModifiedType()) - EmitVLASize(Ty); } else { EnsureInsertPoint(); @@ -608,19 +825,17 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { EHStack.pushCleanup<CallStackRestore>(NormalCleanup, Stack); } - // Get the element type. - const llvm::Type *LElemTy = ConvertTypeForMem(Ty); - const llvm::Type *LElemPtrTy = - LElemTy->getPointerTo(CGM.getContext().getTargetAddressSpace(Ty)); + llvm::Value *elementCount; + QualType elementType; + llvm::tie(elementCount, elementType) = getVLASize(Ty); - llvm::Value *VLASize = EmitVLASize(Ty); + const llvm::Type *llvmTy = ConvertTypeForMem(elementType); // Allocate memory for the array. - llvm::AllocaInst *VLA = - Builder.CreateAlloca(llvm::Type::getInt8Ty(getLLVMContext()), VLASize, "vla"); - VLA->setAlignment(alignment.getQuantity()); + llvm::AllocaInst *vla = Builder.CreateAlloca(llvmTy, elementCount, "vla"); + vla->setAlignment(alignment.getQuantity()); - DeclPtr = Builder.CreateBitCast(VLA, LElemPtrTy, "tmp"); + DeclPtr = vla; } llvm::Value *&DMEntry = LocalDeclMap[&D]; @@ -667,6 +882,21 @@ static bool isCapturedBy(const VarDecl &var, const Expr *e) { return false; } +/// \brief Determine whether the given initializer is trivial in the sense +/// that it requires no code to be generated. +static bool isTrivialInitializer(const Expr *Init) { + if (!Init) + return true; + + if (const CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) + if (CXXConstructorDecl *Constructor = Construct->getConstructor()) + if (Constructor->isTrivial() && + Constructor->isDefaultConstructor() && + !Construct->requiresZeroInitialization()) + return true; + + return false; +} void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { assert(emission.Variable && "emission was not valid!"); @@ -690,7 +920,9 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { if (emission.IsByRef) emitByrefStructureInit(emission); - if (!Init) return; + if (isTrivialInitializer(Init)) + return; + CharUnits alignment = emission.Alignment; @@ -702,8 +934,11 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { llvm::Value *Loc = capturedByInit ? emission.Address : emission.getObjectAddress(*this); - if (!emission.IsConstantAggregate) - return EmitExprAsInit(Init, &D, Loc, alignment, capturedByInit); + if (!emission.IsConstantAggregate) { + LValue lv = MakeAddrLValue(Loc, type, alignment.getQuantity()); + lv.setNonGC(true); + return EmitExprAsInit(Init, &D, lv, capturedByInit); + } // If this is a simple aggregate initialization, we can optimize it // in various ways. @@ -765,32 +1000,87 @@ void CodeGenFunction::EmitAutoVarInit(const AutoVarEmission &emission) { /// \param capturedByInit true if the variable is a __block variable /// whose address is potentially changed by the initializer void CodeGenFunction::EmitExprAsInit(const Expr *init, - const VarDecl *var, - llvm::Value *loc, - CharUnits alignment, + const ValueDecl *D, + LValue lvalue, bool capturedByInit) { - QualType type = var->getType(); - bool isVolatile = type.isVolatileQualified(); + QualType type = D->getType(); if (type->isReferenceType()) { - RValue RV = EmitReferenceBindingToExpr(init, var); - if (capturedByInit) loc = BuildBlockByrefAddress(loc, var); - EmitStoreOfScalar(RV.getScalarVal(), loc, false, - alignment.getQuantity(), type); + RValue rvalue = EmitReferenceBindingToExpr(init, D); + if (capturedByInit) + drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); + EmitStoreThroughLValue(rvalue, lvalue); } else if (!hasAggregateLLVMType(type)) { - llvm::Value *V = EmitScalarExpr(init); - if (capturedByInit) loc = BuildBlockByrefAddress(loc, var); - EmitStoreOfScalar(V, loc, isVolatile, alignment.getQuantity(), type); + EmitScalarInit(init, D, lvalue, capturedByInit); } else if (type->isAnyComplexType()) { ComplexPairTy complex = EmitComplexExpr(init); - if (capturedByInit) loc = BuildBlockByrefAddress(loc, var); - StoreComplexToAddr(complex, loc, isVolatile); + if (capturedByInit) + drillIntoBlockVariable(*this, lvalue, cast<VarDecl>(D)); + StoreComplexToAddr(complex, lvalue.getAddress(), lvalue.isVolatile()); } else { // TODO: how can we delay here if D is captured by its initializer? - EmitAggExpr(init, AggValueSlot::forAddr(loc, isVolatile, true, false)); + EmitAggExpr(init, AggValueSlot::forLValue(lvalue, true, false)); } } +/// Enter a destroy cleanup for the given local variable. +void CodeGenFunction::emitAutoVarTypeCleanup( + const CodeGenFunction::AutoVarEmission &emission, + QualType::DestructionKind dtorKind) { + assert(dtorKind != QualType::DK_none); + + // Note that for __block variables, we want to destroy the + // original stack object, not the possibly forwarded object. + llvm::Value *addr = emission.getObjectAddress(*this); + + const VarDecl *var = emission.Variable; + QualType type = var->getType(); + + CleanupKind cleanupKind = NormalAndEHCleanup; + CodeGenFunction::Destroyer *destroyer = 0; + + switch (dtorKind) { + case QualType::DK_none: + llvm_unreachable("no cleanup for trivially-destructible variable"); + + case QualType::DK_cxx_destructor: + // If there's an NRVO flag on the emission, we need a different + // cleanup. + if (emission.NRVOFlag) { + assert(!type->isArrayType()); + CXXDestructorDecl *dtor = type->getAsCXXRecordDecl()->getDestructor(); + EHStack.pushCleanup<DestroyNRVOVariable>(cleanupKind, addr, dtor, + emission.NRVOFlag); + return; + } + break; + + case QualType::DK_objc_strong_lifetime: + // Suppress cleanups for pseudo-strong variables. + if (var->isARCPseudoStrong()) return; + + // Otherwise, consider whether to use an EH cleanup or not. + cleanupKind = getARCCleanupKind(); + + // Use the imprecise destroyer by default. + if (!var->hasAttr<ObjCPreciseLifetimeAttr>()) + destroyer = CodeGenFunction::destroyARCStrongImprecise; + break; + + case QualType::DK_objc_weak_lifetime: + break; + } + + // If we haven't chosen a more specific destroyer, use the default. + if (!destroyer) destroyer = &getDestroyer(dtorKind); + + // Use an EH cleanup in array destructors iff the destructor itself + // is being pushed as an EH cleanup. + bool useEHCleanup = (cleanupKind & EHCleanup); + EHStack.pushCleanup<DestroyObject>(cleanupKind, addr, type, destroyer, + useEHCleanup); +} + void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { assert(emission.Variable && "emission was not valid!"); @@ -799,35 +1089,14 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { const VarDecl &D = *emission.Variable; - // Handle C++ destruction of variables. - if (getLangOptions().CPlusPlus) { - QualType type = D.getType(); - QualType baseType = getContext().getBaseElementType(type); - if (const RecordType *RT = baseType->getAs<RecordType>()) { - CXXRecordDecl *ClassDecl = cast<CXXRecordDecl>(RT->getDecl()); - if (!ClassDecl->hasTrivialDestructor()) { - // Note: We suppress the destructor call when the corresponding NRVO - // flag has been set. - - // Note that for __block variables, we want to destroy the - // original stack object, not the possible forwarded object. - llvm::Value *Loc = emission.getObjectAddress(*this); - - const CXXDestructorDecl *D = ClassDecl->getDestructor(); - assert(D && "EmitLocalBlockVarDecl - destructor is nul"); - - if (type != baseType) { - const ConstantArrayType *Array = - getContext().getAsConstantArrayType(type); - assert(Array && "types changed without array?"); - EHStack.pushCleanup<CallArrayDtor>(NormalAndEHCleanup, - D, Array, Loc); - } else { - EHStack.pushCleanup<CallVarDtor>(NormalAndEHCleanup, - D, emission.NRVOFlag, Loc); - } - } - } + // Check the type for a cleanup. + if (QualType::DestructionKind dtorKind = D.getType().isDestructedType()) + emitAutoVarTypeCleanup(emission, dtorKind); + + // In GC mode, honor objc_precise_lifetime. + if (getLangOptions().getGCMode() != LangOptions::NonGC && + D.hasAttr<ObjCPreciseLifetimeAttr>()) { + EHStack.pushCleanup<ExtendGCLifetime>(NormalCleanup, &D); } // Handle the cleanup attribute. @@ -847,6 +1116,271 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) { enterByrefCleanup(emission); } +CodeGenFunction::Destroyer & +CodeGenFunction::getDestroyer(QualType::DestructionKind kind) { + // This is surprisingly compiler-dependent. GCC 4.2 can't bind + // references to functions directly in returns, and using '*&foo' + // confuses MSVC. Luckily, the following code pattern works in both. + Destroyer *destroyer = 0; + switch (kind) { + case QualType::DK_none: llvm_unreachable("no destroyer for trivial dtor"); + case QualType::DK_cxx_destructor: + destroyer = &destroyCXXObject; + break; + case QualType::DK_objc_strong_lifetime: + destroyer = &destroyARCStrongPrecise; + break; + case QualType::DK_objc_weak_lifetime: + destroyer = &destroyARCWeak; + break; + } + return *destroyer; +} + +/// pushDestroy - Push the standard destructor for the given type. +void CodeGenFunction::pushDestroy(QualType::DestructionKind dtorKind, + llvm::Value *addr, QualType type) { + assert(dtorKind && "cannot push destructor for trivial type"); + + CleanupKind cleanupKind = getCleanupKind(dtorKind); + pushDestroy(cleanupKind, addr, type, getDestroyer(dtorKind), + cleanupKind & EHCleanup); +} + +void CodeGenFunction::pushDestroy(CleanupKind cleanupKind, llvm::Value *addr, + QualType type, Destroyer &destroyer, + bool useEHCleanupForArray) { + pushFullExprCleanup<DestroyObject>(cleanupKind, addr, type, + destroyer, useEHCleanupForArray); +} + +/// emitDestroy - Immediately perform the destruction of the given +/// object. +/// +/// \param addr - the address of the object; a type* +/// \param type - the type of the object; if an array type, all +/// objects are destroyed in reverse order +/// \param destroyer - the function to call to destroy individual +/// elements +/// \param useEHCleanupForArray - whether an EH cleanup should be +/// used when destroying array elements, in case one of the +/// destructions throws an exception +void CodeGenFunction::emitDestroy(llvm::Value *addr, QualType type, + Destroyer &destroyer, + bool useEHCleanupForArray) { + const ArrayType *arrayType = getContext().getAsArrayType(type); + if (!arrayType) + return destroyer(*this, addr, type); + + llvm::Value *begin = addr; + llvm::Value *length = emitArrayLength(arrayType, type, begin); + + // Normally we have to check whether the array is zero-length. + bool checkZeroLength = true; + + // But if the array length is constant, we can suppress that. + if (llvm::ConstantInt *constLength = dyn_cast<llvm::ConstantInt>(length)) { + // ...and if it's constant zero, we can just skip the entire thing. + if (constLength->isZero()) return; + checkZeroLength = false; + } + + llvm::Value *end = Builder.CreateInBoundsGEP(begin, length); + emitArrayDestroy(begin, end, type, destroyer, + checkZeroLength, useEHCleanupForArray); +} + +/// emitArrayDestroy - Destroys all the elements of the given array, +/// beginning from last to first. The array cannot be zero-length. +/// +/// \param begin - a type* denoting the first element of the array +/// \param end - a type* denoting one past the end of the array +/// \param type - the element type of the array +/// \param destroyer - the function to call to destroy elements +/// \param useEHCleanup - whether to push an EH cleanup to destroy +/// the remaining elements in case the destruction of a single +/// element throws +void CodeGenFunction::emitArrayDestroy(llvm::Value *begin, + llvm::Value *end, + QualType type, + Destroyer &destroyer, + bool checkZeroLength, + bool useEHCleanup) { + assert(!type->isArrayType()); + + // The basic structure here is a do-while loop, because we don't + // need to check for the zero-element case. + llvm::BasicBlock *bodyBB = createBasicBlock("arraydestroy.body"); + llvm::BasicBlock *doneBB = createBasicBlock("arraydestroy.done"); + + if (checkZeroLength) { + llvm::Value *isEmpty = Builder.CreateICmpEQ(begin, end, + "arraydestroy.isempty"); + Builder.CreateCondBr(isEmpty, doneBB, bodyBB); + } + + // Enter the loop body, making that address the current address. + llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); + EmitBlock(bodyBB); + llvm::PHINode *elementPast = + Builder.CreatePHI(begin->getType(), 2, "arraydestroy.elementPast"); + elementPast->addIncoming(end, entryBB); + + // Shift the address back by one element. + llvm::Value *negativeOne = llvm::ConstantInt::get(SizeTy, -1, true); + llvm::Value *element = Builder.CreateInBoundsGEP(elementPast, negativeOne, + "arraydestroy.element"); + + if (useEHCleanup) + pushRegularPartialArrayCleanup(begin, element, type, destroyer); + + // Perform the actual destruction there. + destroyer(*this, element, type); + + if (useEHCleanup) + PopCleanupBlock(); + + // Check whether we've reached the end. + llvm::Value *done = Builder.CreateICmpEQ(element, begin, "arraydestroy.done"); + Builder.CreateCondBr(done, doneBB, bodyBB); + elementPast->addIncoming(element, Builder.GetInsertBlock()); + + // Done. + EmitBlock(doneBB); +} + +/// Perform partial array destruction as if in an EH cleanup. Unlike +/// emitArrayDestroy, the element type here may still be an array type. +static void emitPartialArrayDestroy(CodeGenFunction &CGF, + llvm::Value *begin, llvm::Value *end, + QualType type, + CodeGenFunction::Destroyer &destroyer) { + // If the element type is itself an array, drill down. + unsigned arrayDepth = 0; + while (const ArrayType *arrayType = CGF.getContext().getAsArrayType(type)) { + // VLAs don't require a GEP index to walk into. + if (!isa<VariableArrayType>(arrayType)) + arrayDepth++; + type = arrayType->getElementType(); + } + + if (arrayDepth) { + llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, arrayDepth+1); + + llvm::SmallVector<llvm::Value*,4> gepIndices(arrayDepth, zero); + begin = CGF.Builder.CreateInBoundsGEP(begin, gepIndices.begin(), + gepIndices.end(), "pad.arraybegin"); + end = CGF.Builder.CreateInBoundsGEP(end, gepIndices.begin(), + gepIndices.end(), "pad.arrayend"); + } + + // Destroy the array. We don't ever need an EH cleanup because we + // assume that we're in an EH cleanup ourselves, so a throwing + // destructor causes an immediate terminate. + CGF.emitArrayDestroy(begin, end, type, destroyer, + /*checkZeroLength*/ true, /*useEHCleanup*/ false); +} + +namespace { + /// RegularPartialArrayDestroy - a cleanup which performs a partial + /// array destroy where the end pointer is regularly determined and + /// does not need to be loaded from a local. + class RegularPartialArrayDestroy : public EHScopeStack::Cleanup { + llvm::Value *ArrayBegin; + llvm::Value *ArrayEnd; + QualType ElementType; + CodeGenFunction::Destroyer &Destroyer; + public: + RegularPartialArrayDestroy(llvm::Value *arrayBegin, llvm::Value *arrayEnd, + QualType elementType, + CodeGenFunction::Destroyer *destroyer) + : ArrayBegin(arrayBegin), ArrayEnd(arrayEnd), + ElementType(elementType), Destroyer(*destroyer) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + emitPartialArrayDestroy(CGF, ArrayBegin, ArrayEnd, + ElementType, Destroyer); + } + }; + + /// IrregularPartialArrayDestroy - a cleanup which performs a + /// partial array destroy where the end pointer is irregularly + /// determined and must be loaded from a local. + class IrregularPartialArrayDestroy : public EHScopeStack::Cleanup { + llvm::Value *ArrayBegin; + llvm::Value *ArrayEndPointer; + QualType ElementType; + CodeGenFunction::Destroyer &Destroyer; + public: + IrregularPartialArrayDestroy(llvm::Value *arrayBegin, + llvm::Value *arrayEndPointer, + QualType elementType, + CodeGenFunction::Destroyer *destroyer) + : ArrayBegin(arrayBegin), ArrayEndPointer(arrayEndPointer), + ElementType(elementType), Destroyer(*destroyer) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + llvm::Value *arrayEnd = CGF.Builder.CreateLoad(ArrayEndPointer); + emitPartialArrayDestroy(CGF, ArrayBegin, arrayEnd, + ElementType, Destroyer); + } + }; +} + +/// pushIrregularPartialArrayCleanup - Push an EH cleanup to destroy +/// already-constructed elements of the given array. The cleanup +/// may be popped with DeactivateCleanupBlock or PopCleanupBlock. +/// +/// \param elementType - the immediate element type of the array; +/// possibly still an array type +/// \param array - a value of type elementType* +/// \param destructionKind - the kind of destruction required +/// \param initializedElementCount - a value of type size_t* holding +/// the number of successfully-constructed elements +void CodeGenFunction::pushIrregularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEndPointer, + QualType elementType, + Destroyer &destroyer) { + pushFullExprCleanup<IrregularPartialArrayDestroy>(EHCleanup, + arrayBegin, arrayEndPointer, + elementType, &destroyer); +} + +/// pushRegularPartialArrayCleanup - Push an EH cleanup to destroy +/// already-constructed elements of the given array. The cleanup +/// may be popped with DeactivateCleanupBlock or PopCleanupBlock. +/// +/// \param elementType - the immediate element type of the array; +/// possibly still an array type +/// \param array - a value of type elementType* +/// \param destructionKind - the kind of destruction required +/// \param initializedElementCount - a value of type size_t* holding +/// the number of successfully-constructed elements +void CodeGenFunction::pushRegularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEnd, + QualType elementType, + Destroyer &destroyer) { + pushFullExprCleanup<RegularPartialArrayDestroy>(EHCleanup, + arrayBegin, arrayEnd, + elementType, &destroyer); +} + +namespace { + /// A cleanup to perform a release of an object at the end of a + /// function. This is used to balance out the incoming +1 of a + /// ns_consumed argument when we can't reasonably do that just by + /// not doing the initial retain for a __block argument. + struct ConsumeARCParameter : EHScopeStack::Cleanup { + ConsumeARCParameter(llvm::Value *param) : Param(param) {} + + llvm::Value *Param; + + void Emit(CodeGenFunction &CGF, Flags flags) { + CGF.EmitARCRelease(Param, /*precise*/ false); + } + }; +} + /// Emit an alloca (or GlobalValue depending on target) /// for the specified parameter and set up LocalDeclMap. void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, @@ -883,10 +1417,56 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, // Otherwise, create a temporary to hold the value. DeclPtr = CreateMemTemp(Ty, D.getName() + ".addr"); + bool doStore = true; + + Qualifiers qs = Ty.getQualifiers(); + + if (Qualifiers::ObjCLifetime lt = qs.getObjCLifetime()) { + // We honor __attribute__((ns_consumed)) for types with lifetime. + // For __strong, it's handled by just skipping the initial retain; + // otherwise we have to balance out the initial +1 with an extra + // cleanup to do the release at the end of the function. + bool isConsumed = D.hasAttr<NSConsumedAttr>(); + + // 'self' is always formally __strong, but if this is not an + // init method then we don't want to retain it. + if (D.isARCPseudoStrong()) { + const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CurCodeDecl); + assert(&D == method->getSelfDecl()); + assert(lt == Qualifiers::OCL_Strong); + assert(qs.hasConst()); + assert(method->getMethodFamily() != OMF_init); + (void) method; + lt = Qualifiers::OCL_ExplicitNone; + } + + if (lt == Qualifiers::OCL_Strong) { + if (!isConsumed) + // Don't use objc_retainBlock for block pointers, because we + // don't want to Block_copy something just because we got it + // as a parameter. + Arg = EmitARCRetainNonBlock(Arg); + } else { + // Push the cleanup for a consumed parameter. + if (isConsumed) + EHStack.pushCleanup<ConsumeARCParameter>(getARCCleanupKind(), Arg); + + if (lt == Qualifiers::OCL_Weak) { + EmitARCInitWeak(DeclPtr, Arg); + doStore = false; // The weak init is a store, no need to do two + } + } + + // Enter the cleanup scope. + EmitAutoVarWithLifetime(*this, D, DeclPtr, lt); + } + // Store the initial value into the alloca. - EmitStoreOfScalar(Arg, DeclPtr, Ty.isVolatileQualified(), - getContext().getDeclAlign(&D).getQuantity(), Ty, - CGM.getTBAAInfo(Ty)); + if (doStore) { + LValue lv = MakeAddrLValue(DeclPtr, Ty, + getContext().getDeclAlign(&D).getQuantity()); + EmitStoreOfScalar(Arg, lv); + } } llvm::Value *&DMEntry = LocalDeclMap[&D]; @@ -894,8 +1474,6 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, llvm::Value *Arg, DMEntry = DeclPtr; // Emit debug info for param declaration. - if (CGDebugInfo *DI = getDebugInfo()) { - DI->setLocation(D.getLocation()); + if (CGDebugInfo *DI = getDebugInfo()) DI->EmitDeclareOfArgVariable(&D, DeclPtr, ArgNo, Builder); - } } diff --git a/lib/CodeGen/CGDeclCXX.cpp b/lib/CodeGen/CGDeclCXX.cpp index 178badd44d81..0ae6a3d2ee90 100644 --- a/lib/CodeGen/CGDeclCXX.cpp +++ b/lib/CodeGen/CGDeclCXX.cpp @@ -27,68 +27,76 @@ static void EmitDeclInit(CodeGenFunction &CGF, const VarDecl &D, "Should not call EmitDeclInit on a reference!"); ASTContext &Context = CGF.getContext(); - - const Expr *Init = D.getInit(); - QualType T = D.getType(); - bool isVolatile = Context.getCanonicalType(T).isVolatileQualified(); - unsigned Alignment = Context.getDeclAlign(&D).getQuantity(); - if (!CGF.hasAggregateLLVMType(T)) { - llvm::Value *V = CGF.EmitScalarExpr(Init); + unsigned alignment = Context.getDeclAlign(&D).getQuantity(); + QualType type = D.getType(); + LValue lv = CGF.MakeAddrLValue(DeclPtr, type, alignment); + + const Expr *Init = D.getInit(); + if (!CGF.hasAggregateLLVMType(type)) { CodeGenModule &CGM = CGF.CGM; - Qualifiers::GC GCAttr = CGM.getContext().getObjCGCAttrKind(T); - if (GCAttr == Qualifiers::Strong) - CGM.getObjCRuntime().EmitObjCGlobalAssign(CGF, V, DeclPtr, - D.isThreadSpecified()); - else if (GCAttr == Qualifiers::Weak) - CGM.getObjCRuntime().EmitObjCWeakAssign(CGF, V, DeclPtr); + if (lv.isObjCStrong()) + CGM.getObjCRuntime().EmitObjCGlobalAssign(CGF, CGF.EmitScalarExpr(Init), + DeclPtr, D.isThreadSpecified()); + else if (lv.isObjCWeak()) + CGM.getObjCRuntime().EmitObjCWeakAssign(CGF, CGF.EmitScalarExpr(Init), + DeclPtr); else - CGF.EmitStoreOfScalar(V, DeclPtr, isVolatile, Alignment, T); - } else if (T->isAnyComplexType()) { - CGF.EmitComplexExprIntoAddr(Init, DeclPtr, isVolatile); + CGF.EmitScalarInit(Init, &D, lv, false); + } else if (type->isAnyComplexType()) { + CGF.EmitComplexExprIntoAddr(Init, DeclPtr, lv.isVolatile()); } else { - CGF.EmitAggExpr(Init, AggValueSlot::forAddr(DeclPtr, isVolatile, true)); + CGF.EmitAggExpr(Init, AggValueSlot::forLValue(lv, true)); } } /// Emit code to cause the destruction of the given variable with /// static storage duration. static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D, - llvm::Constant *DeclPtr) { + llvm::Constant *addr) { CodeGenModule &CGM = CGF.CGM; - ASTContext &Context = CGF.getContext(); - - QualType T = D.getType(); - - // Drill down past array types. - const ConstantArrayType *Array = Context.getAsConstantArrayType(T); - if (Array) - T = Context.getBaseElementType(Array); + + // FIXME: __attribute__((cleanup)) ? - /// If that's not a record, we're done. - /// FIXME: __attribute__((cleanup)) ? - const RecordType *RT = T->getAs<RecordType>(); - if (!RT) + QualType type = D.getType(); + QualType::DestructionKind dtorKind = type.isDestructedType(); + + switch (dtorKind) { + case QualType::DK_none: return; - - CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); - if (RD->hasTrivialDestructor()) + + case QualType::DK_cxx_destructor: + break; + + case QualType::DK_objc_strong_lifetime: + case QualType::DK_objc_weak_lifetime: + // We don't care about releasing objects during process teardown. return; - - CXXDestructorDecl *Dtor = RD->getDestructor(); - - llvm::Constant *DtorFn; - if (Array) { - DtorFn = - CodeGenFunction(CGM).GenerateCXXAggrDestructorHelper(Dtor, Array, - DeclPtr); - const llvm::Type *Int8PtrTy = - llvm::Type::getInt8PtrTy(CGM.getLLVMContext()); - DeclPtr = llvm::Constant::getNullValue(Int8PtrTy); - } else - DtorFn = CGM.GetAddrOfCXXDestructor(Dtor, Dtor_Complete); - - CGF.EmitCXXGlobalDtorRegistration(DtorFn, DeclPtr); + } + + llvm::Constant *function; + llvm::Constant *argument; + + // Special-case non-array C++ destructors, where there's a function + // with the right signature that we can just call. + const CXXRecordDecl *record = 0; + if (dtorKind == QualType::DK_cxx_destructor && + (record = type->getAsCXXRecordDecl())) { + assert(!record->hasTrivialDestructor()); + CXXDestructorDecl *dtor = record->getDestructor(); + + function = CGM.GetAddrOfCXXDestructor(dtor, Dtor_Complete); + argument = addr; + + // Otherwise, the standard logic requires a helper function. + } else { + function = CodeGenFunction(CGM).generateDestroyHelper(addr, type, + CGF.getDestroyer(dtorKind), + CGF.needsEHCleanup(dtorKind)); + argument = llvm::Constant::getNullValue(CGF.Int8PtrTy); + } + + CGF.EmitCXXGlobalDtorRegistration(function, argument); } void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D, @@ -118,12 +126,13 @@ CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, } // Get the destructor function type - const llvm::Type *DtorFnTy = + llvm::Type *ArgTys[] = { Int8PtrTy }; + llvm::Type *DtorFnTy = llvm::FunctionType::get(llvm::Type::getVoidTy(getLLVMContext()), - Int8PtrTy, false); + ArgTys, false); DtorFnTy = llvm::PointerType::getUnqual(DtorFnTy); - const llvm::Type *Params[] = { DtorFnTy, Int8PtrTy, Int8PtrTy }; + llvm::Type *Params[] = { DtorFnTy, Int8PtrTy, Int8PtrTy }; // Get the __cxa_atexit function type // extern "C" int __cxa_atexit ( void (*f)(void *), void *p, void *d ); @@ -140,7 +149,7 @@ CodeGenFunction::EmitCXXGlobalDtorRegistration(llvm::Constant *DtorFn, llvm::Value *Args[3] = { llvm::ConstantExpr::getBitCast(DtorFn, DtorFnTy), llvm::ConstantExpr::getBitCast(DeclPtr, Int8PtrTy), llvm::ConstantExpr::getBitCast(Handle, Int8PtrTy) }; - Builder.CreateCall(AtExitFn, &Args[0], llvm::array_endof(Args)); + Builder.CreateCall(AtExitFn, Args); } void CodeGenFunction::EmitCXXGuardedInit(const VarDecl &D, @@ -270,12 +279,11 @@ void CodeGenFunction::GenerateCXXGlobalVarDeclInitFunc(llvm::Function *Fn, getTypes().getNullaryFunctionInfo(), FunctionArgList(), SourceLocation()); - // Use guarded initialization if the global variable is weak due to - // being a class template's static data member. These will always - // have weak_odr linkage. - if (Addr->getLinkage() == llvm::GlobalValue::WeakODRLinkage && - D->isStaticDataMember() && - D->getInstantiatedFromStaticDataMember()) { + // Use guarded initialization if the global variable is weak. This + // occurs for, e.g., instantiated static data members and + // definitions explicitly marked weak. + if (Addr->getLinkage() == llvm::GlobalValue::WeakODRLinkage || + Addr->getLinkage() == llvm::GlobalValue::WeakAnyLinkage) { EmitCXXGuardedInit(*D, Addr); } else { EmitCXXGlobalVarDeclInit(*D, Addr); @@ -291,10 +299,21 @@ void CodeGenFunction::GenerateCXXGlobalInitFunc(llvm::Function *Fn, getTypes().getNullaryFunctionInfo(), FunctionArgList(), SourceLocation()); + RunCleanupsScope Scope(*this); + + // When building in Objective-C++ ARC mode, create an autorelease pool + // around the global initializers. + if (getLangOptions().ObjCAutoRefCount && getLangOptions().CPlusPlus) { + llvm::Value *token = EmitObjCAutoreleasePoolPush(); + EmitObjCAutoreleasePoolCleanup(token); + } + for (unsigned i = 0; i != NumDecls; ++i) if (Decls[i]) - Builder.CreateCall(Decls[i]); + Builder.CreateCall(Decls[i]); + Scope.ForceCleanup(); + FinishFunction(); } @@ -318,13 +337,13 @@ void CodeGenFunction::GenerateCXXGlobalDtorFunc(llvm::Function *Fn, FinishFunction(); } -/// GenerateCXXAggrDestructorHelper - Generates a helper function which when -/// invoked, calls the default destructor on array elements in reverse order of -/// construction. +/// generateDestroyHelper - Generates a helper function which, when +/// invoked, destroys the given object. llvm::Function * -CodeGenFunction::GenerateCXXAggrDestructorHelper(const CXXDestructorDecl *D, - const ArrayType *Array, - llvm::Value *This) { +CodeGenFunction::generateDestroyHelper(llvm::Constant *addr, + QualType type, + Destroyer &destroyer, + bool useEHCleanupForArray) { FunctionArgList args; ImplicitParamDecl dst(0, SourceLocation(), 0, getContext().VoidPtrTy); args.push_back(&dst); @@ -333,19 +352,16 @@ CodeGenFunction::GenerateCXXAggrDestructorHelper(const CXXDestructorDecl *D, CGM.getTypes().getFunctionInfo(getContext().VoidTy, args, FunctionType::ExtInfo()); const llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI, false); - llvm::Function *Fn = + llvm::Function *fn = CreateGlobalInitOrDestructFunction(CGM, FTy, "__cxx_global_array_dtor"); - StartFunction(GlobalDecl(), getContext().VoidTy, Fn, FI, args, + StartFunction(GlobalDecl(), getContext().VoidTy, fn, FI, args, SourceLocation()); - QualType BaseElementTy = getContext().getBaseElementType(Array); - const llvm::Type *BasePtr = ConvertType(BaseElementTy)->getPointerTo(); - llvm::Value *BaseAddrPtr = Builder.CreateBitCast(This, BasePtr); - - EmitCXXAggrDestructorCall(D, Array, BaseAddrPtr); + emitDestroy(addr, type, destroyer, useEHCleanupForArray); FinishFunction(); - return Fn; + return fn; } + diff --git a/lib/CodeGen/CGException.cpp b/lib/CodeGen/CGException.cpp index e8ad6da2f980..418bea6ee402 100644 --- a/lib/CodeGen/CGException.cpp +++ b/lib/CodeGen/CGException.cpp @@ -29,10 +29,9 @@ using namespace CodeGen; static llvm::Constant *getAllocateExceptionFn(CodeGenFunction &CGF) { // void *__cxa_allocate_exception(size_t thrown_size); - const llvm::Type *SizeTy = CGF.ConvertType(CGF.getContext().getSizeType()); + llvm::Type *ArgTys[] = { CGF.SizeTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getInt8PtrTy(CGF.getLLVMContext()), - SizeTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.Int8PtrTy, ArgTys, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_allocate_exception"); } @@ -40,10 +39,9 @@ static llvm::Constant *getAllocateExceptionFn(CodeGenFunction &CGF) { static llvm::Constant *getFreeExceptionFn(CodeGenFunction &CGF) { // void __cxa_free_exception(void *thrown_exception); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); + llvm::Type *ArgTys[] = { CGF.Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - Int8PtrTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.VoidTy, ArgTys, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_free_exception"); } @@ -52,11 +50,9 @@ static llvm::Constant *getThrowFn(CodeGenFunction &CGF) { // void __cxa_throw(void *thrown_exception, std::type_info *tinfo, // void (*dest) (void *)); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); - const llvm::Type *Args[3] = { Int8PtrTy, Int8PtrTy, Int8PtrTy }; + llvm::Type *Args[3] = { CGF.Int8PtrTy, CGF.Int8PtrTy, CGF.Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - Args, /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.VoidTy, Args, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_throw"); } @@ -65,8 +61,7 @@ static llvm::Constant *getReThrowFn(CodeGenFunction &CGF) { // void __cxa_rethrow(); const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.VoidTy, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_rethrow"); } @@ -74,9 +69,9 @@ static llvm::Constant *getReThrowFn(CodeGenFunction &CGF) { static llvm::Constant *getGetExceptionPtrFn(CodeGenFunction &CGF) { // void *__cxa_get_exception_ptr(void*); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); + llvm::Type *ArgTys[] = { CGF.Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(Int8PtrTy, Int8PtrTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.Int8PtrTy, ArgTys, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_get_exception_ptr"); } @@ -84,9 +79,9 @@ static llvm::Constant *getGetExceptionPtrFn(CodeGenFunction &CGF) { static llvm::Constant *getBeginCatchFn(CodeGenFunction &CGF) { // void *__cxa_begin_catch(void*); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); + llvm::Type *ArgTys[] = { CGF.Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(Int8PtrTy, Int8PtrTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.Int8PtrTy, ArgTys, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_begin_catch"); } @@ -95,8 +90,7 @@ static llvm::Constant *getEndCatchFn(CodeGenFunction &CGF) { // void __cxa_end_catch(); const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.VoidTy, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_end_catch"); } @@ -104,17 +98,17 @@ static llvm::Constant *getEndCatchFn(CodeGenFunction &CGF) { static llvm::Constant *getUnexpectedFn(CodeGenFunction &CGF) { // void __cxa_call_unexepcted(void *thrown_exception); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); + llvm::Type *ArgTys[] = { CGF.Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - Int8PtrTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.VoidTy, ArgTys, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, "__cxa_call_unexpected"); } llvm::Constant *CodeGenFunction::getUnwindResumeFn() { + llvm::Type *ArgTys[] = { Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(VoidTy, Int8PtrTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(VoidTy, ArgTys, /*IsVarArgs=*/false); if (CGM.getLangOptions().SjLjExceptions) return CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume"); @@ -122,8 +116,9 @@ llvm::Constant *CodeGenFunction::getUnwindResumeFn() { } llvm::Constant *CodeGenFunction::getUnwindResumeOrRethrowFn() { + llvm::Type *ArgTys[] = { Int8PtrTy }; const llvm::FunctionType *FTy = - llvm::FunctionType::get(VoidTy, Int8PtrTy, /*IsVarArgs=*/false); + llvm::FunctionType::get(VoidTy, ArgTys, /*IsVarArgs=*/false); if (CGM.getLangOptions().SjLjExceptions) return CGM.CreateRuntimeFunction(FTy, "_Unwind_SjLj_Resume_or_Rethrow"); @@ -134,20 +129,26 @@ static llvm::Constant *getTerminateFn(CodeGenFunction &CGF) { // void __terminate(); const llvm::FunctionType *FTy = - llvm::FunctionType::get(llvm::Type::getVoidTy(CGF.getLLVMContext()), - /*IsVarArgs=*/false); + llvm::FunctionType::get(CGF.VoidTy, /*IsVarArgs=*/false); - return CGF.CGM.CreateRuntimeFunction(FTy, - CGF.CGM.getLangOptions().CPlusPlus ? "_ZSt9terminatev" : "abort"); + llvm::StringRef name; + + // In C++, use std::terminate(). + if (CGF.getLangOptions().CPlusPlus) + name = "_ZSt9terminatev"; // FIXME: mangling! + else if (CGF.getLangOptions().ObjC1 && + CGF.CGM.getCodeGenOpts().ObjCRuntimeHasTerminate) + name = "objc_terminate"; + else + name = "abort"; + return CGF.CGM.CreateRuntimeFunction(FTy, name); } static llvm::Constant *getCatchallRethrowFn(CodeGenFunction &CGF, llvm::StringRef Name) { - const llvm::Type *Int8PtrTy = - llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); - const llvm::Type *VoidTy = llvm::Type::getVoidTy(CGF.getLLVMContext()); - const llvm::FunctionType *FTy = llvm::FunctionType::get(VoidTy, Int8PtrTy, - /*IsVarArgs=*/false); + llvm::Type *ArgTys[] = { CGF.Int8PtrTy }; + const llvm::FunctionType *FTy = + llvm::FunctionType::get(CGF.VoidTy, ArgTys, /*IsVarArgs=*/false); return CGF.CGM.CreateRuntimeFunction(FTy, Name); } @@ -322,9 +323,10 @@ static llvm::Constant *getCleanupValue(CodeGenFunction &CGF) { namespace { /// A cleanup to free the exception object if its initialization /// throws. - struct FreeException { - static void Emit(CodeGenFunction &CGF, bool forEH, - llvm::Value *exn) { + struct FreeException : EHScopeStack::Cleanup { + llvm::Value *exn; + FreeException(llvm::Value *exn) : exn(exn) {} + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.Builder.CreateCall(getFreeExceptionFn(CGF), exn) ->setDoesNotThrow(); } @@ -354,7 +356,8 @@ static void EmitAnyExprToExn(CodeGenFunction &CGF, const Expr *e, // evaluated but before the exception is caught. But the best way // to handle that is to teach EmitAggExpr to do the final copy // differently if it can't be elided. - CGF.EmitAnyExprToMem(e, typedAddr, /*Volatile*/ false, /*IsInit*/ true); + CGF.EmitAnyExprToMem(e, typedAddr, e->getType().getQualifiers(), + /*IsInit*/ true); // Deactivate the cleanup block. CGF.DeactivateCleanupBlock(cleanup); @@ -407,7 +410,6 @@ void CodeGenFunction::EmitCXXThrowExpr(const CXXThrowExpr *E) { EmitAnyExprToExn(*this, E->getSubExpr(), ExceptionPtr); // Now throw the exception. - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(getLLVMContext()); llvm::Constant *TypeInfo = CGM.GetAddrOfRTTIDescriptor(ThrowType, /*ForEH=*/true); @@ -786,7 +788,7 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { // Tell the backend how to generate the landing pad. llvm::CallInst *Selection = Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::eh_selector), - EHSelector.begin(), EHSelector.end(), "eh.selector"); + EHSelector, "eh.selector"); Selection->setDoesNotThrow(); // Save the selector value in mandatory-cleanup mode. @@ -920,13 +922,13 @@ namespace { CallEndCatch(bool MightThrow) : MightThrow(MightThrow) {} bool MightThrow; - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { if (!MightThrow) { CGF.Builder.CreateCall(getEndCatchFn(CGF))->setDoesNotThrow(); return; } - CGF.EmitCallOrInvoke(getEndCatchFn(CGF), 0, 0); + CGF.EmitCallOrInvoke(getEndCatchFn(CGF)); } }; } @@ -1084,7 +1086,8 @@ static void InitCatchParam(CodeGenFunction &CGF, CGF.EHStack.pushTerminate(); // Perform the copy construction. - CGF.EmitAggExpr(copyExpr, AggValueSlot::forAddr(ParamAddr, false, false)); + CGF.EmitAggExpr(copyExpr, AggValueSlot::forAddr(ParamAddr, Qualifiers(), + false)); // Leave the terminate scope. CGF.EHStack.popTerminate(); @@ -1137,8 +1140,8 @@ static void BeginCatch(CodeGenFunction &CGF, const CXXCatchStmt *S) { namespace { struct CallRethrow : EHScopeStack::Cleanup { - void Emit(CodeGenFunction &CGF, bool IsForEH) { - CGF.EmitCallOrInvoke(getReThrowFn(CGF), 0, 0); + void Emit(CodeGenFunction &CGF, Flags flags) { + CGF.EmitCallOrInvoke(getReThrowFn(CGF)); } }; } @@ -1209,7 +1212,7 @@ namespace { CallEndCatchForFinally(llvm::Value *ForEHVar, llvm::Value *EndCatchFn) : ForEHVar(ForEHVar), EndCatchFn(EndCatchFn) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { llvm::BasicBlock *EndCatchBB = CGF.createBasicBlock("finally.endcatch"); llvm::BasicBlock *CleanupContBB = CGF.createBasicBlock("finally.cleanup.cont"); @@ -1218,7 +1221,7 @@ namespace { CGF.Builder.CreateLoad(ForEHVar, "finally.endcatch"); CGF.Builder.CreateCondBr(ShouldEndCatch, EndCatchBB, CleanupContBB); CGF.EmitBlock(EndCatchBB); - CGF.EmitCallOrInvoke(EndCatchFn, 0, 0); // catch-all, so might throw + CGF.EmitCallOrInvoke(EndCatchFn); // catch-all, so might throw CGF.EmitBlock(CleanupContBB); } }; @@ -1236,7 +1239,7 @@ namespace { : Body(Body), ForEHVar(ForEHVar), EndCatchFn(EndCatchFn), RethrowFn(RethrowFn), SavedExnVar(SavedExnVar) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { // Enter a cleanup to call the end-catch function if one was provided. if (EndCatchFn) CGF.EHStack.pushCleanup<CallEndCatchForFinally>(NormalAndEHCleanup, @@ -1263,10 +1266,9 @@ namespace { CGF.EmitBlock(RethrowBB); if (SavedExnVar) { - llvm::Value *Args[] = { CGF.Builder.CreateLoad(SavedExnVar) }; - CGF.EmitCallOrInvoke(RethrowFn, Args, Args+1); + CGF.EmitCallOrInvoke(RethrowFn, CGF.Builder.CreateLoad(SavedExnVar)); } else { - CGF.EmitCallOrInvoke(RethrowFn, 0, 0); + CGF.EmitCallOrInvoke(RethrowFn); } CGF.Builder.CreateUnreachable(); @@ -1296,14 +1298,16 @@ namespace { /// Enters a finally block for an implementation using zero-cost /// exceptions. This is mostly general, but hard-codes some /// language/ABI-specific behavior in the catch-all sections. -CodeGenFunction::FinallyInfo -CodeGenFunction::EnterFinallyBlock(const Stmt *Body, - llvm::Constant *BeginCatchFn, - llvm::Constant *EndCatchFn, - llvm::Constant *RethrowFn) { - assert((BeginCatchFn != 0) == (EndCatchFn != 0) && +void CodeGenFunction::FinallyInfo::enter(CodeGenFunction &CGF, + const Stmt *body, + llvm::Constant *beginCatchFn, + llvm::Constant *endCatchFn, + llvm::Constant *rethrowFn) { + assert((beginCatchFn != 0) == (endCatchFn != 0) && "begin/end catch functions not paired"); - assert(RethrowFn && "rethrow function is required"); + assert(rethrowFn && "rethrow function is required"); + + BeginCatchFn = beginCatchFn; // The rethrow function has one of the following two types: // void (*)() @@ -1311,13 +1315,12 @@ CodeGenFunction::EnterFinallyBlock(const Stmt *Body, // In the latter case we need to pass it the exception object. // But we can't use the exception slot because the @finally might // have a landing pad (which would overwrite the exception slot). - const llvm::FunctionType *RethrowFnTy = + const llvm::FunctionType *rethrowFnTy = cast<llvm::FunctionType>( - cast<llvm::PointerType>(RethrowFn->getType()) - ->getElementType()); - llvm::Value *SavedExnVar = 0; - if (RethrowFnTy->getNumParams()) - SavedExnVar = CreateTempAlloca(Builder.getInt8PtrTy(), "finally.exn"); + cast<llvm::PointerType>(rethrowFn->getType())->getElementType()); + SavedExnVar = 0; + if (rethrowFnTy->getNumParams()) + SavedExnVar = CGF.CreateTempAlloca(CGF.Int8PtrTy, "finally.exn"); // A finally block is a statement which must be executed on any edge // out of a given scope. Unlike a cleanup, the finally block may @@ -1331,67 +1334,64 @@ CodeGenFunction::EnterFinallyBlock(const Stmt *Body, // The finally block itself is generated in the context of a cleanup // which conditionally leaves the catch-all. - FinallyInfo Info; - // Jump destination for performing the finally block on an exception // edge. We'll never actually reach this block, so unreachable is // fine. - JumpDest RethrowDest = getJumpDestInCurrentScope(getUnreachableBlock()); + RethrowDest = CGF.getJumpDestInCurrentScope(CGF.getUnreachableBlock()); // Whether the finally block is being executed for EH purposes. - llvm::AllocaInst *ForEHVar = CreateTempAlloca(Builder.getInt1Ty(), - "finally.for-eh"); - InitTempAlloca(ForEHVar, llvm::ConstantInt::getFalse(getLLVMContext())); + ForEHVar = CGF.CreateTempAlloca(CGF.Builder.getInt1Ty(), "finally.for-eh"); + CGF.Builder.CreateStore(CGF.Builder.getFalse(), ForEHVar); // Enter a normal cleanup which will perform the @finally block. - EHStack.pushCleanup<PerformFinally>(NormalCleanup, Body, - ForEHVar, EndCatchFn, - RethrowFn, SavedExnVar); + CGF.EHStack.pushCleanup<PerformFinally>(NormalCleanup, body, + ForEHVar, endCatchFn, + rethrowFn, SavedExnVar); // Enter a catch-all scope. - llvm::BasicBlock *CatchAllBB = createBasicBlock("finally.catchall"); - CGBuilderTy::InsertPoint SavedIP = Builder.saveIP(); - Builder.SetInsertPoint(CatchAllBB); - - // If there's a begin-catch function, call it. - if (BeginCatchFn) { - Builder.CreateCall(BeginCatchFn, Builder.CreateLoad(getExceptionSlot())) - ->setDoesNotThrow(); - } - - // If we need to remember the exception pointer to rethrow later, do so. - if (SavedExnVar) { - llvm::Value *SavedExn = Builder.CreateLoad(getExceptionSlot()); - Builder.CreateStore(SavedExn, SavedExnVar); - } + llvm::BasicBlock *catchBB = CGF.createBasicBlock("finally.catchall"); + EHCatchScope *catchScope = CGF.EHStack.pushCatch(1); + catchScope->setCatchAllHandler(0, catchBB); +} - // Tell the finally block that we're in EH. - Builder.CreateStore(llvm::ConstantInt::getTrue(getLLVMContext()), ForEHVar); +void CodeGenFunction::FinallyInfo::exit(CodeGenFunction &CGF) { + // Leave the finally catch-all. + EHCatchScope &catchScope = cast<EHCatchScope>(*CGF.EHStack.begin()); + llvm::BasicBlock *catchBB = catchScope.getHandler(0).Block; + CGF.EHStack.popCatch(); - // Thread a jump through the finally cleanup. - EmitBranchThroughCleanup(RethrowDest); + // If there are any references to the catch-all block, emit it. + if (catchBB->use_empty()) { + delete catchBB; + } else { + CGBuilderTy::InsertPoint savedIP = CGF.Builder.saveAndClearIP(); + CGF.EmitBlock(catchBB); - Builder.restoreIP(SavedIP); + llvm::Value *exn = 0; - EHCatchScope *CatchScope = EHStack.pushCatch(1); - CatchScope->setCatchAllHandler(0, CatchAllBB); + // If there's a begin-catch function, call it. + if (BeginCatchFn) { + exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot()); + CGF.Builder.CreateCall(BeginCatchFn, exn)->setDoesNotThrow(); + } - return Info; -} + // If we need to remember the exception pointer to rethrow later, do so. + if (SavedExnVar) { + if (!exn) exn = CGF.Builder.CreateLoad(CGF.getExceptionSlot()); + CGF.Builder.CreateStore(exn, SavedExnVar); + } -void CodeGenFunction::ExitFinallyBlock(FinallyInfo &Info) { - // Leave the finally catch-all. - EHCatchScope &Catch = cast<EHCatchScope>(*EHStack.begin()); - llvm::BasicBlock *CatchAllBB = Catch.getHandler(0).Block; - EHStack.popCatch(); + // Tell the cleanups in the finally block that we're do this for EH. + CGF.Builder.CreateStore(CGF.Builder.getTrue(), ForEHVar); - // And leave the normal cleanup. - PopCleanupBlock(); + // Thread a jump through the finally cleanup. + CGF.EmitBranchThroughCleanup(RethrowDest); - CGBuilderTy::InsertPoint SavedIP = Builder.saveAndClearIP(); - EmitBlock(CatchAllBB, true); + CGF.Builder.restoreIP(savedIP); + } - Builder.restoreIP(SavedIP); + // Finally, leave the @finally cleanup. + CGF.PopCleanupBlock(); } llvm::BasicBlock *CodeGenFunction::getTerminateLandingPad() { @@ -1416,7 +1416,7 @@ llvm::BasicBlock *CodeGenFunction::getTerminateLandingPad() { llvm::Value *Args[3] = { Exn, getOpaquePersonalityFn(CGM, Personality), getCatchAllValue(*this) }; Builder.CreateCall(CGM.getIntrinsic(llvm::Intrinsic::eh_selector), - Args, Args+3, "eh.selector") + Args, "eh.selector") ->setDoesNotThrow(); llvm::CallInst *TerminateCall = Builder.CreateCall(getTerminateFn(*this)); diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 2f6b55bd7b0b..a7e8003eaab5 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -20,8 +20,8 @@ #include "CGObjCRuntime.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" -#include "llvm/Intrinsics.h" #include "clang/Frontend/CodeGenOptions.h" +#include "llvm/Intrinsics.h" #include "llvm/Target/TargetData.h" using namespace clang; using namespace CodeGen; @@ -131,16 +131,16 @@ RValue CodeGenFunction::EmitAnyExprToTemp(const Expr *E) { /// location. void CodeGenFunction::EmitAnyExprToMem(const Expr *E, llvm::Value *Location, - bool IsLocationVolatile, + Qualifiers Quals, bool IsInit) { - if (E->getType()->isComplexType()) - EmitComplexExprIntoAddr(E, Location, IsLocationVolatile); + if (E->getType()->isAnyComplexType()) + EmitComplexExprIntoAddr(E, Location, Quals.hasVolatile()); else if (hasAggregateLLVMType(E->getType())) - EmitAggExpr(E, AggValueSlot::forAddr(Location, IsLocationVolatile, IsInit)); + EmitAggExpr(E, AggValueSlot::forAddr(Location, Quals, IsInit)); else { RValue RV = RValue::get(EmitScalarExpr(E, /*Ignore*/ false)); LValue LV = MakeAddrLValue(Location, E->getType()); - EmitStoreThroughLValue(RV, LV, E->getType()); + EmitStoreThroughLValue(RV, LV); } } @@ -203,7 +203,24 @@ static llvm::Value * EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, llvm::Value *&ReferenceTemporary, const CXXDestructorDecl *&ReferenceTemporaryDtor, + QualType &ObjCARCReferenceLifetimeType, const NamedDecl *InitializedDecl) { + // Look through expressions for materialized temporaries (for now). + if (const MaterializeTemporaryExpr *M + = dyn_cast<MaterializeTemporaryExpr>(E)) { + // Objective-C++ ARC: + // If we are binding a reference to a temporary that has ownership, we + // need to perform retain/release operations on the temporary. + if (CGF.getContext().getLangOptions().ObjCAutoRefCount && + E->getType()->isObjCLifetimeType() && + (E->getType().getObjCLifetime() == Qualifiers::OCL_Strong || + E->getType().getObjCLifetime() == Qualifiers::OCL_Weak || + E->getType().getObjCLifetime() == Qualifiers::OCL_Autoreleasing)) + ObjCARCReferenceLifetimeType = E->getType(); + + E = M->GetTemporaryExpr(); + } + if (const CXXDefaultArgExpr *DAE = dyn_cast<CXXDefaultArgExpr>(E)) E = DAE->getExpr(); @@ -213,6 +230,7 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, return EmitExprForReferenceBinding(CGF, TE->getSubExpr(), ReferenceTemporary, ReferenceTemporaryDtor, + ObjCARCReferenceLifetimeType, InitializedDecl); } @@ -229,12 +247,69 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, RV = CGF.EmitLoadOfPropertyRefLValue(LV); return RV.getScalarVal(); } + if (LV.isSimple()) return LV.getAddress(); // We have to load the lvalue. - RV = CGF.EmitLoadOfLValue(LV, E->getType()); + RV = CGF.EmitLoadOfLValue(LV); } else { + if (!ObjCARCReferenceLifetimeType.isNull()) { + ReferenceTemporary = CreateReferenceTemporary(CGF, + ObjCARCReferenceLifetimeType, + InitializedDecl); + + + LValue RefTempDst = CGF.MakeAddrLValue(ReferenceTemporary, + ObjCARCReferenceLifetimeType); + + CGF.EmitScalarInit(E, dyn_cast_or_null<ValueDecl>(InitializedDecl), + RefTempDst, false); + + bool ExtendsLifeOfTemporary = false; + if (const VarDecl *Var = dyn_cast_or_null<VarDecl>(InitializedDecl)) { + if (Var->extendsLifetimeOfTemporary()) + ExtendsLifeOfTemporary = true; + } else if (InitializedDecl && isa<FieldDecl>(InitializedDecl)) { + ExtendsLifeOfTemporary = true; + } + + if (!ExtendsLifeOfTemporary) { + // Since the lifetime of this temporary isn't going to be extended, + // we need to clean it up ourselves at the end of the full expression. + switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: { + assert(!ObjCARCReferenceLifetimeType->isArrayType()); + CleanupKind cleanupKind = CGF.getARCCleanupKind(); + CGF.pushDestroy(cleanupKind, + ReferenceTemporary, + ObjCARCReferenceLifetimeType, + CodeGenFunction::destroyARCStrongImprecise, + cleanupKind & EHCleanup); + break; + } + + case Qualifiers::OCL_Weak: + assert(!ObjCARCReferenceLifetimeType->isArrayType()); + CGF.pushDestroy(NormalAndEHCleanup, + ReferenceTemporary, + ObjCARCReferenceLifetimeType, + CodeGenFunction::destroyARCWeak, + /*useEHCleanupForArray*/ true); + break; + } + + ObjCARCReferenceLifetimeType = QualType(); + } + + return ReferenceTemporary; + } + llvm::SmallVector<SubobjectAdjustment, 2> Adjustments; while (true) { E = E->IgnoreParens(); @@ -279,12 +354,10 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, !E->getType()->isAnyComplexType()) { ReferenceTemporary = CreateReferenceTemporary(CGF, E->getType(), InitializedDecl); - AggSlot = AggValueSlot::forAddr(ReferenceTemporary, false, + AggSlot = AggValueSlot::forAddr(ReferenceTemporary, Qualifiers(), InitializedDecl != 0); } - - RV = CGF.EmitAnyExpr(E, AggSlot); - + if (InitializedDecl) { // Get the destructor for the reference temporary. if (const RecordType *RT = E->getType()->getAs<RecordType>()) { @@ -294,6 +367,8 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, } } + RV = CGF.EmitAnyExpr(E, AggSlot); + // Check if need to perform derived-to-base casts and/or field accesses, to // get from the temporary object we created (and, potentially, for which we // extended the lifetime) to the subobject we're binding the reference to. @@ -326,7 +401,7 @@ EmitExprForReferenceBinding(CodeGenFunction &CGF, const Expr *E, Object = CreateReferenceTemporary(CGF, T, InitializedDecl); LValue TempLV = CGF.MakeAddrLValue(Object, Adjustment.Field->getType()); - CGF.EmitStoreThroughLValue(CGF.EmitLoadOfLValue(LV, T), TempLV, T); + CGF.EmitStoreThroughLValue(CGF.EmitLoadOfLValue(LV), TempLV); break; } @@ -361,26 +436,65 @@ CodeGenFunction::EmitReferenceBindingToExpr(const Expr *E, const NamedDecl *InitializedDecl) { llvm::Value *ReferenceTemporary = 0; const CXXDestructorDecl *ReferenceTemporaryDtor = 0; + QualType ObjCARCReferenceLifetimeType; llvm::Value *Value = EmitExprForReferenceBinding(*this, E, ReferenceTemporary, ReferenceTemporaryDtor, + ObjCARCReferenceLifetimeType, InitializedDecl); - if (!ReferenceTemporaryDtor) + if (!ReferenceTemporaryDtor && ObjCARCReferenceLifetimeType.isNull()) return RValue::get(Value); // Make sure to call the destructor for the reference temporary. - if (const VarDecl *VD = dyn_cast_or_null<VarDecl>(InitializedDecl)) { - if (VD->hasGlobalStorage()) { + const VarDecl *VD = dyn_cast_or_null<VarDecl>(InitializedDecl); + if (VD && VD->hasGlobalStorage()) { + if (ReferenceTemporaryDtor) { llvm::Constant *DtorFn = CGM.GetAddrOfCXXDestructor(ReferenceTemporaryDtor, Dtor_Complete); EmitCXXGlobalDtorRegistration(DtorFn, cast<llvm::Constant>(ReferenceTemporary)); - - return RValue::get(Value); + } else { + assert(!ObjCARCReferenceLifetimeType.isNull()); + // Note: We intentionally do not register a global "destructor" to + // release the object. } + + return RValue::get(Value); } - PushDestructorCleanup(ReferenceTemporaryDtor, ReferenceTemporary); - + if (ReferenceTemporaryDtor) + PushDestructorCleanup(ReferenceTemporaryDtor, ReferenceTemporary); + else { + switch (ObjCARCReferenceLifetimeType.getObjCLifetime()) { + case Qualifiers::OCL_None: + assert(0 && "Not a reference temporary that needs to be deallocated"); + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + // Nothing to do. + break; + + case Qualifiers::OCL_Strong: { + bool precise = VD && VD->hasAttr<ObjCPreciseLifetimeAttr>(); + CleanupKind cleanupKind = getARCCleanupKind(); + // This local is a GCC and MSVC compiler workaround. + Destroyer *destroyer = precise ? &destroyARCStrongPrecise : + &destroyARCStrongImprecise; + pushDestroy(cleanupKind, ReferenceTemporary, ObjCARCReferenceLifetimeType, + *destroyer, cleanupKind & EHCleanup); + break; + } + + case Qualifiers::OCL_Weak: { + // This local is a GCC and MSVC compiler workaround. + Destroyer *destroyer = &destroyARCWeak; + // __weak objects always get EH cleanups; otherwise, exceptions + // could cause really nasty crashes instead of mere leaks. + pushDestroy(NormalAndEHCleanup, ReferenceTemporary, + ObjCARCReferenceLifetimeType, *destroyer, true); + break; + } + } + } + return RValue::get(Value); } @@ -402,8 +516,7 @@ void CodeGenFunction::EmitCheck(llvm::Value *Address, unsigned Size) { // This needs to be to the standard address space. Address = Builder.CreateBitCast(Address, Int8PtrTy); - const llvm::Type *IntPtrT = IntPtrTy; - llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::objectsize, &IntPtrT, 1); + llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::objectsize, IntPtrTy); // In time, people may want to control this and use a 1 here. llvm::Value *Arg = Builder.getFalse(); @@ -592,6 +705,8 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { return EmitLValue(cast<ChooseExpr>(E)->getChosenSubExpr(getContext())); case Expr::OpaqueValueExprClass: return EmitOpaqueValueLValue(cast<OpaqueValueExpr>(E)); + case Expr::SubstNonTypeTemplateParmExprClass: + return EmitLValue(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement()); case Expr::ImplicitCastExprClass: case Expr::CStyleCastExprClass: case Expr::CXXFunctionalCastExprClass: @@ -599,10 +714,20 @@ LValue CodeGenFunction::EmitLValue(const Expr *E) { case Expr::CXXDynamicCastExprClass: case Expr::CXXReinterpretCastExprClass: case Expr::CXXConstCastExprClass: + case Expr::ObjCBridgedCastExprClass: return EmitCastLValue(cast<CastExpr>(E)); + + case Expr::MaterializeTemporaryExprClass: + return EmitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(E)); } } +llvm::Value *CodeGenFunction::EmitLoadOfScalar(LValue lvalue) { + return EmitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatile(), + lvalue.getAlignment(), lvalue.getType(), + lvalue.getTBAAInfo()); +} + llvm::Value *CodeGenFunction::EmitLoadOfScalar(llvm::Value *Addr, bool Volatile, unsigned Alignment, QualType Ty, llvm::MDNode *TBAAInfo) { @@ -651,6 +776,7 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, llvm::Value *Addr, QualType Ty, llvm::MDNode *TBAAInfo) { Value = EmitToMemory(Value, Ty); + llvm::StoreInst *Store = Builder.CreateStore(Value, Addr, Volatile); if (Alignment) Store->setAlignment(Alignment); @@ -658,29 +784,30 @@ void CodeGenFunction::EmitStoreOfScalar(llvm::Value *Value, llvm::Value *Addr, CGM.DecorateInstruction(Store, TBAAInfo); } +void CodeGenFunction::EmitStoreOfScalar(llvm::Value *value, LValue lvalue) { + EmitStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(), + lvalue.getAlignment(), lvalue.getType(), + lvalue.getTBAAInfo()); +} + /// EmitLoadOfLValue - Given an expression that represents a value lvalue, this /// method emits the address of the lvalue, then loads the result as an rvalue, /// returning the rvalue. -RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, QualType ExprType) { +RValue CodeGenFunction::EmitLoadOfLValue(LValue LV) { if (LV.isObjCWeak()) { // load of a __weak object. llvm::Value *AddrWeakObj = LV.getAddress(); return RValue::get(CGM.getObjCRuntime().EmitObjCWeakRead(*this, AddrWeakObj)); } + if (LV.getQuals().getObjCLifetime() == Qualifiers::OCL_Weak) + return RValue::get(EmitARCLoadWeak(LV.getAddress())); if (LV.isSimple()) { - llvm::Value *Ptr = LV.getAddress(); - - // Functions are l-values that don't require loading. - if (ExprType->isFunctionType()) - return RValue::get(Ptr); + assert(!LV.getType()->isFunctionType()); // Everything needs a load. - return RValue::get(EmitLoadOfScalar(Ptr, LV.isVolatileQualified(), - LV.getAlignment(), ExprType, - LV.getTBAAInfo())); - + return RValue::get(EmitLoadOfScalar(LV)); } if (LV.isVectorElt()) { @@ -693,21 +820,20 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, QualType ExprType) { // If this is a reference to a subset of the elements of a vector, either // shuffle the input or extract/insert them as appropriate. if (LV.isExtVectorElt()) - return EmitLoadOfExtVectorElementLValue(LV, ExprType); + return EmitLoadOfExtVectorElementLValue(LV); if (LV.isBitField()) - return EmitLoadOfBitfieldLValue(LV, ExprType); + return EmitLoadOfBitfieldLValue(LV); assert(LV.isPropertyRef() && "Unknown LValue type!"); return EmitLoadOfPropertyRefLValue(LV); } -RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV, - QualType ExprType) { +RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV) { const CGBitFieldInfo &Info = LV.getBitFieldInfo(); // Get the output type. - const llvm::Type *ResLTy = ConvertType(ExprType); + const llvm::Type *ResLTy = ConvertType(LV.getType()); unsigned ResSizeInBits = CGM.getTargetData().getTypeSizeInBits(ResLTy); // Compute the result as an OR of all of the individual component accesses. @@ -733,7 +859,7 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV, // Cast to the access type. const llvm::Type *PTy = llvm::Type::getIntNPtrTy(getLLVMContext(), AI.AccessWidth, - CGM.getContext().getTargetAddressSpace(ExprType)); + CGM.getContext().getTargetAddressSpace(LV.getType())); Ptr = Builder.CreateBitCast(Ptr, PTy); // Perform the load. @@ -777,8 +903,7 @@ RValue CodeGenFunction::EmitLoadOfBitfieldLValue(LValue LV, // If this is a reference to a subset of the elements of a vector, create an // appropriate shufflevector. -RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV, - QualType ExprType) { +RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV) { llvm::Value *Vec = Builder.CreateLoad(LV.getExtVectorAddr(), LV.isVolatileQualified(), "tmp"); @@ -786,7 +911,7 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV, // If the result of the expression is a non-vector type, we must be extracting // a single element. Just codegen as an extractelement. - const VectorType *ExprVT = ExprType->getAs<VectorType>(); + const VectorType *ExprVT = LV.getType()->getAs<VectorType>(); if (!ExprVT) { unsigned InIdx = getAccessedFieldNo(0, Elts); llvm::Value *Elt = llvm::ConstantInt::get(Int32Ty, InIdx); @@ -813,8 +938,7 @@ RValue CodeGenFunction::EmitLoadOfExtVectorElementLValue(LValue LV, /// EmitStoreThroughLValue - Store the specified rvalue into the specified /// lvalue, where both are guaranteed to the have the same type, and that type /// is 'Ty'. -void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, - QualType Ty) { +void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst) { if (!Dst.isSimple()) { if (Dst.isVectorElt()) { // Read/modify/write the vector, inserting the new element. @@ -829,15 +953,41 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, // If this is an update of extended vector elements, insert them as // appropriate. if (Dst.isExtVectorElt()) - return EmitStoreThroughExtVectorComponentLValue(Src, Dst, Ty); + return EmitStoreThroughExtVectorComponentLValue(Src, Dst); if (Dst.isBitField()) - return EmitStoreThroughBitfieldLValue(Src, Dst, Ty); + return EmitStoreThroughBitfieldLValue(Src, Dst); assert(Dst.isPropertyRef() && "Unknown LValue type"); return EmitStoreThroughPropertyRefLValue(Src, Dst); } + // There's special magic for assigning into an ARC-qualified l-value. + if (Qualifiers::ObjCLifetime Lifetime = Dst.getQuals().getObjCLifetime()) { + switch (Lifetime) { + case Qualifiers::OCL_None: + llvm_unreachable("present but none"); + + case Qualifiers::OCL_ExplicitNone: + // nothing special + break; + + case Qualifiers::OCL_Strong: + EmitARCStoreStrong(Dst, Src.getScalarVal(), /*ignore*/ true); + return; + + case Qualifiers::OCL_Weak: + EmitARCStoreWeak(Dst.getAddress(), Src.getScalarVal(), /*ignore*/ true); + return; + + case Qualifiers::OCL_Autoreleasing: + Src = RValue::get(EmitObjCExtendObjectLifetime(Dst.getType(), + Src.getScalarVal())); + // fall into the normal path + break; + } + } + if (Dst.isObjCWeak() && !Dst.isNonGC()) { // load of a __weak object. llvm::Value *LvalueDst = Dst.getAddress(); @@ -871,24 +1021,21 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst, } assert(Src.isScalar() && "Can't emit an agg store with this method"); - EmitStoreOfScalar(Src.getScalarVal(), Dst.getAddress(), - Dst.isVolatileQualified(), Dst.getAlignment(), Ty, - Dst.getTBAAInfo()); + EmitStoreOfScalar(Src.getScalarVal(), Dst); } void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, - QualType Ty, llvm::Value **Result) { const CGBitFieldInfo &Info = Dst.getBitFieldInfo(); // Get the output type. - const llvm::Type *ResLTy = ConvertTypeForMem(Ty); + const llvm::Type *ResLTy = ConvertTypeForMem(Dst.getType()); unsigned ResSizeInBits = CGM.getTargetData().getTypeSizeInBits(ResLTy); // Get the source value, truncated to the width of the bit-field. llvm::Value *SrcVal = Src.getScalarVal(); - if (Ty->isBooleanType()) + if (Dst.getType()->isBooleanType()) SrcVal = Builder.CreateIntCast(SrcVal, ResLTy, /*IsSigned=*/false); SrcVal = Builder.CreateAnd(SrcVal, llvm::APInt::getLowBitsSet(ResSizeInBits, @@ -983,8 +1130,7 @@ void CodeGenFunction::EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, } void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, - LValue Dst, - QualType Ty) { + LValue Dst) { // This access turns into a read/modify/write of the vector. Load the input // value now. llvm::Value *Vec = Builder.CreateLoad(Dst.getExtVectorAddr(), @@ -993,7 +1139,7 @@ void CodeGenFunction::EmitStoreThroughExtVectorComponentLValue(RValue Src, llvm::Value *SrcVal = Src.getScalarVal(); - if (const VectorType *VTy = Ty->getAs<VectorType>()) { + if (const VectorType *VTy = Dst.getType()->getAs<VectorType>()) { unsigned NumSrcElts = VTy->getNumElements(); unsigned NumDstElts = cast<llvm::VectorType>(Vec->getType())->getNumElements(); @@ -1113,7 +1259,12 @@ static void setObjCGCLValueClass(const ASTContext &Ctx, const Expr *E, setObjCGCLValueClass(Ctx, Exp->getSubExpr(), LV); return; } - + + if (const ObjCBridgedCastExpr *Exp = dyn_cast<ObjCBridgedCastExpr>(E)) { + setObjCGCLValueClass(Ctx, Exp->getSubExpr(), LV); + return; + } + if (const ArraySubscriptExpr *Exp = dyn_cast<ArraySubscriptExpr>(E)) { setObjCGCLValueClass(Ctx, Exp->getBase(), LV); if (LV.isObjCIvar() && !LV.isObjCArray()) @@ -1136,6 +1287,14 @@ static void setObjCGCLValueClass(const ASTContext &Ctx, const Expr *E, } } +static llvm::Value * +EmitBitCastOfLValueToProperType(CodeGenFunction &CGF, + llvm::Value *V, llvm::Type *IRType, + llvm::StringRef Name = llvm::StringRef()) { + unsigned AS = cast<llvm::PointerType>(V->getType())->getAddressSpace(); + return CGF.Builder.CreateBitCast(V, IRType->getPointerTo(AS), Name); +} + static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF, const Expr *E, const VarDecl *VD) { assert((VD->hasExternalStorage() || VD->isFileVarDecl()) && @@ -1144,6 +1303,10 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF, llvm::Value *V = CGF.CGM.GetAddrOfGlobalVar(VD); if (VD->getType()->isReferenceType()) V = CGF.Builder.CreateLoad(V, "tmp"); + + V = EmitBitCastOfLValueToProperType(CGF, V, + CGF.getTypes().ConvertTypeForMem(E->getType())); + unsigned Alignment = CGF.getContext().getDeclAlign(VD).getQuantity(); LValue LV = CGF.MakeAddrLValue(V, E->getType(), Alignment); setObjCGCLValueClass(CGF.getContext(), E, LV); @@ -1151,7 +1314,7 @@ static LValue EmitGlobalVarDeclLValue(CodeGenFunction &CGF, } static LValue EmitFunctionDeclLValue(CodeGenFunction &CGF, - const Expr *E, const FunctionDecl *FD) { + const Expr *E, const FunctionDecl *FD) { llvm::Value *V = CGF.CGM.GetAddrOfFunction(FD); if (!FD->hasPrototype()) { if (const FunctionProtoType *Proto = @@ -1200,6 +1363,9 @@ LValue CodeGenFunction::EmitDeclRefLValue(const DeclRefExpr *E) { if (VD->getType()->isReferenceType()) V = Builder.CreateLoad(V, "tmp"); + V = EmitBitCastOfLValueToProperType(*this, V, + getTypes().ConvertTypeForMem(E->getType())); + LValue LV = MakeAddrLValue(V, E->getType(), Alignment); if (NonGCable) { LV.getQuals().removeObjCGCAttr(); @@ -1359,7 +1525,7 @@ llvm::BasicBlock *CodeGenFunction::getTrapBB() { TrapBB = createBasicBlock("trap"); EmitBlock(TrapBB); - llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::trap, 0, 0); + llvm::Value *F = CGM.getIntrinsic(llvm::Intrinsic::trap); llvm::CallInst *TrapCall = Builder.CreateCall(F); TrapCall->setDoesNotReturn(); TrapCall->setDoesNotThrow(); @@ -1400,7 +1566,7 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E) { assert(LHS.isSimple() && "Can only subscript lvalue vectors here!"); Idx = Builder.CreateIntCast(Idx, Int32Ty, IdxSigned, "vidx"); return LValue::MakeVectorElt(LHS.getAddress(), Idx, - E->getBase()->getType().getCVRQualifiers()); + E->getBase()->getType()); } // Extend or truncate the index type to 32 or 64-bits. @@ -1430,21 +1596,27 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E) { // size is a VLA or Objective-C interface. llvm::Value *Address = 0; unsigned ArrayAlignment = 0; - if (const VariableArrayType *VAT = + if (const VariableArrayType *vla = getContext().getAsVariableArrayType(E->getType())) { - llvm::Value *VLASize = GetVLASize(VAT); - - Idx = Builder.CreateMul(Idx, VLASize); - - // The base must be a pointer, which is not an aggregate. Emit it. - llvm::Value *Base = EmitScalarExpr(E->getBase()); - - Address = EmitCastToVoidPtr(Base); - if (getContext().getLangOptions().isSignedOverflowDefined()) + // The base must be a pointer, which is not an aggregate. Emit + // it. It needs to be emitted first in case it's what captures + // the VLA bounds. + Address = EmitScalarExpr(E->getBase()); + + // The element count here is the total number of non-VLA elements. + llvm::Value *numElements = getVLASize(vla).first; + + // Effectively, the multiply by the VLA size is part of the GEP. + // GEP indexes are signed, and scaling an index isn't permitted to + // signed-overflow, so we use the same semantics for our explicit + // multiply. We suppress this if overflow is not undefined behavior. + if (getLangOptions().isSignedOverflowDefined()) { + Idx = Builder.CreateMul(Idx, numElements); Address = Builder.CreateGEP(Address, Idx, "arrayidx"); - else + } else { + Idx = Builder.CreateNSWMul(Idx, numElements); Address = Builder.CreateInBoundsGEP(Address, Idx, "arrayidx"); - Address = Builder.CreateBitCast(Address, Base->getType()); + } } else if (const ObjCObjectType *OIT = E->getType()->getAs<ObjCObjectType>()){ // Indexing over an interface, as in "NSString *P; P[4];" llvm::Value *InterfaceSize = @@ -1539,7 +1711,7 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { Base = EmitLValue(E->getBase()); } else { // Otherwise, the base is a normal rvalue (as in (V+V).x), emit it as such. - assert(E->getBase()->getType()->getAs<VectorType>() && + assert(E->getBase()->getType()->isVectorType() && "Result must be a vector"); llvm::Value *Vec = EmitScalarExpr(E->getBase()); @@ -1548,6 +1720,9 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { Builder.CreateStore(Vec, VecMem); Base = MakeAddrLValue(VecMem, E->getBase()->getType()); } + + QualType type = + E->getType().withCVRQualifiers(Base.getQuals().getCVRQualifiers()); // Encode the element access list into a vector of unsigned indices. llvm::SmallVector<unsigned, 4> Indices; @@ -1555,8 +1730,7 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { if (Base.isSimple()) { llvm::Constant *CV = GenerateConstantVector(getLLVMContext(), Indices); - return LValue::MakeExtVectorElt(Base.getAddress(), CV, - Base.getVRQualifiers()); + return LValue::MakeExtVectorElt(Base.getAddress(), CV, type); } assert(Base.isExtVectorElt() && "Can only subscript lvalue vec elts here!"); @@ -1570,8 +1744,7 @@ EmitExtVectorElementExpr(const ExtVectorElementExpr *E) { CElts.push_back(cast<llvm::Constant>(BaseElts->getOperand(Indices[i]))); } llvm::Constant *CV = llvm::ConstantVector::get(CElts); - return LValue::MakeExtVectorElt(Base.getExtVectorAddr(), CV, - Base.getVRQualifiers()); + return LValue::MakeExtVectorElt(Base.getExtVectorAddr(), CV, type); } LValue CodeGenFunction::EmitMemberExpr(const MemberExpr *E) { @@ -1622,7 +1795,7 @@ LValue CodeGenFunction::EmitLValueForBitfield(llvm::Value *BaseValue, CGM.getTypes().getCGRecordLayout(Field->getParent()); const CGBitFieldInfo &Info = RL.getBitFieldInfo(Field); return LValue::MakeBitfield(BaseValue, Info, - Field->getType().getCVRQualifiers()|CVRQualifiers); + Field->getType().withCVRQualifiers(CVRQualifiers)); } /// EmitLValueForAnonRecordField - Given that the field is a member of @@ -1656,20 +1829,14 @@ LValue CodeGenFunction::EmitLValueForField(llvm::Value *baseAddr, bool mayAlias = rec->hasAttr<MayAliasAttr>(); - llvm::Value *addr; + llvm::Value *addr = baseAddr; if (rec->isUnion()) { - // For unions, we just cast to the appropriate type. + // For unions, there is no pointer adjustment. assert(!type->isReferenceType() && "union has reference member"); - - const llvm::Type *llvmType = CGM.getTypes().ConvertTypeForMem(type); - unsigned AS = - cast<llvm::PointerType>(baseAddr->getType())->getAddressSpace(); - addr = Builder.CreateBitCast(baseAddr, llvmType->getPointerTo(AS), - field->getName()); } else { // For structs, we GEP to the field that the record layout suggests. unsigned idx = CGM.getTypes().getCGRecordLayout(rec).getLLVMFieldNo(field); - addr = Builder.CreateStructGEP(baseAddr, idx, field->getName()); + addr = Builder.CreateStructGEP(addr, idx, field->getName()); // If this is a reference field, load the reference right now. if (const ReferenceType *refType = type->getAs<ReferenceType>()) { @@ -1691,6 +1858,14 @@ LValue CodeGenFunction::EmitLValueForField(llvm::Value *baseAddr, cvr = 0; // qualifiers don't recursively apply to referencee } } + + // Make sure that the address is pointing to the right type. This is critical + // for both unions and structs. A union needs a bitcast, a struct element + // will need a bitcast if the LLVM type laid out doesn't match the desired + // type. + addr = EmitBitCastOfLValueToProperType(*this, addr, + CGM.getTypes().ConvertTypeForMem(type), + field->getName()); unsigned alignment = getContext().getDeclAlign(field).getQuantity(); LValue LV = MakeAddrLValue(addr, type, alignment); @@ -1722,9 +1897,17 @@ CodeGenFunction::EmitLValueForFieldInitialization(llvm::Value *BaseValue, CGM.getTypes().getCGRecordLayout(Field->getParent()); unsigned idx = RL.getLLVMFieldNo(Field); llvm::Value *V = Builder.CreateStructGEP(BaseValue, idx, "tmp"); - assert(!FieldType.getObjCGCAttr() && "fields cannot have GC attrs"); + + // Make sure that the address is pointing to the right type. This is critical + // for both unions and structs. A union needs a bitcast, a struct element + // will need a bitcast if the LLVM type laid out doesn't match the desired + // type. + const llvm::Type *llvmType = ConvertTypeForMem(FieldType); + unsigned AS = cast<llvm::PointerType>(V->getType())->getAddressSpace(); + V = Builder.CreateBitCast(V, llvmType->getPointerTo(AS)); + unsigned Alignment = getContext().getDeclAlign(Field).getQuantity(); return MakeAddrLValue(V, FieldType, Alignment); } @@ -1734,7 +1917,8 @@ LValue CodeGenFunction::EmitCompoundLiteralLValue(const CompoundLiteralExpr *E){ const Expr *InitExpr = E->getInitializer(); LValue Result = MakeAddrLValue(DeclPtr, E->getType()); - EmitAnyExprToMem(InitExpr, DeclPtr, /*Volatile*/ false, /*Init*/ true); + EmitAnyExprToMem(InitExpr, DeclPtr, E->getType().getQualifiers(), + /*Init*/ true); return Result; } @@ -1863,13 +2047,16 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { case CK_DerivedToBaseMemberPointer: case CK_BaseToDerivedMemberPointer: case CK_MemberPointerToBoolean: - case CK_AnyPointerToBlockPointerCast: { + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: { // These casts only produce lvalues when we're binding a reference to a // temporary realized from a (converted) pure rvalue. Emit the expression // as a value, copy it into a temporary, and return an lvalue referring to // that temporary. llvm::Value *V = CreateMemTemp(E->getType(), "ref.temp"); - EmitAnyExprToMem(E, V, false, false); + EmitAnyExprToMem(E, V, E->getType().getQualifiers(), false); return MakeAddrLValue(V, E->getType()); } @@ -1954,11 +2141,18 @@ LValue CodeGenFunction::EmitOpaqueValueLValue(const OpaqueValueExpr *e) { return getOpaqueLValueMapping(e); } +LValue CodeGenFunction::EmitMaterializeTemporaryExpr( + const MaterializeTemporaryExpr *E) { + RValue RV = EmitReferenceBindingToExpr(E->GetTemporaryExpr(), + /*InitializedDecl=*/0); + return MakeAddrLValue(RV.getScalarVal(), E->getType()); +} + + //===--------------------------------------------------------------------===// // Expression Emission //===--------------------------------------------------------------------===// - RValue CodeGenFunction::EmitCallExpr(const CallExpr *E, ReturnValueSlot ReturnValue) { if (CGDebugInfo *DI = getDebugInfo()) { @@ -1988,13 +2182,57 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E, if (const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(TargetDecl)) return EmitCXXOperatorMemberCallExpr(CE, MD, ReturnValue); - if (isa<CXXPseudoDestructorExpr>(E->getCallee()->IgnoreParens())) { - // C++ [expr.pseudo]p1: - // The result shall only be used as the operand for the function call - // operator (), and the result of such a call has type void. The only - // effect is the evaluation of the postfix-expression before the dot or - // arrow. - EmitScalarExpr(E->getCallee()); + if (const CXXPseudoDestructorExpr *PseudoDtor + = dyn_cast<CXXPseudoDestructorExpr>(E->getCallee()->IgnoreParens())) { + QualType DestroyedType = PseudoDtor->getDestroyedType(); + if (getContext().getLangOptions().ObjCAutoRefCount && + DestroyedType->isObjCLifetimeType() && + (DestroyedType.getObjCLifetime() == Qualifiers::OCL_Strong || + DestroyedType.getObjCLifetime() == Qualifiers::OCL_Weak)) { + // Automatic Reference Counting: + // If the pseudo-expression names a retainable object with weak or + // strong lifetime, the object shall be released. + Expr *BaseExpr = PseudoDtor->getBase(); + llvm::Value *BaseValue = NULL; + Qualifiers BaseQuals; + + // If this is s.x, emit s as an lvalue. If it is s->x, emit s as a scalar. + if (PseudoDtor->isArrow()) { + BaseValue = EmitScalarExpr(BaseExpr); + const PointerType *PTy = BaseExpr->getType()->getAs<PointerType>(); + BaseQuals = PTy->getPointeeType().getQualifiers(); + } else { + LValue BaseLV = EmitLValue(BaseExpr); + BaseValue = BaseLV.getAddress(); + QualType BaseTy = BaseExpr->getType(); + BaseQuals = BaseTy.getQualifiers(); + } + + switch (PseudoDtor->getDestroyedType().getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + EmitARCRelease(Builder.CreateLoad(BaseValue, + PseudoDtor->getDestroyedType().isVolatileQualified()), + /*precise*/ true); + break; + + case Qualifiers::OCL_Weak: + EmitARCDestroyWeak(BaseValue); + break; + } + } else { + // C++ [expr.pseudo]p1: + // The result shall only be used as the operand for the function call + // operator (), and the result of such a call has type void. The only + // effect is the evaluation of the postfix-expression before the dot or + // arrow. + EmitScalarExpr(E->getCallee()); + } + return RValue::get(0); } @@ -2016,12 +2254,28 @@ LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) { return EmitPointerToDataMemberBinaryExpr(E); assert(E->getOpcode() == BO_Assign && "unexpected binary l-value"); + + // Note that in all of these cases, __block variables need the RHS + // evaluated first just in case the variable gets moved by the RHS. if (!hasAggregateLLVMType(E->getType())) { - // __block variables need the RHS evaluated first. + switch (E->getLHS()->getType().getObjCLifetime()) { + case Qualifiers::OCL_Strong: + return EmitARCStoreStrong(E, /*ignored*/ false).first; + + case Qualifiers::OCL_Autoreleasing: + return EmitARCStoreAutoreleasing(E).first; + + // No reason to do any of these differently. + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Weak: + break; + } + RValue RV = EmitAnyExpr(E->getRHS()); LValue LV = EmitLValue(E->getLHS()); - EmitStoreThroughLValue(RV, LV, E->getType()); + EmitStoreThroughLValue(RV, LV); return LV; } diff --git a/lib/CodeGen/CGExprAgg.cpp b/lib/CodeGen/CGExprAgg.cpp index d8da642a6b38..915ffd6034e2 100644 --- a/lib/CodeGen/CGExprAgg.cpp +++ b/lib/CodeGen/CGExprAgg.cpp @@ -85,15 +85,16 @@ public: Visit(GE->getResultExpr()); } void VisitUnaryExtension(UnaryOperator *E) { Visit(E->getSubExpr()); } + void VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E) { + return Visit(E->getReplacement()); + } // l-values. void VisitDeclRefExpr(DeclRefExpr *DRE) { EmitAggLoadOfLValue(DRE); } void VisitMemberExpr(MemberExpr *ME) { EmitAggLoadOfLValue(ME); } void VisitUnaryDeref(UnaryOperator *E) { EmitAggLoadOfLValue(E); } void VisitStringLiteral(StringLiteral *E) { EmitAggLoadOfLValue(E); } - void VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { - EmitAggLoadOfLValue(E); - } + void VisitCompoundLiteralExpr(CompoundLiteralExpr *E); void VisitArraySubscriptExpr(ArraySubscriptExpr *E) { EmitAggLoadOfLValue(E); } @@ -131,13 +132,13 @@ public: void VisitExprWithCleanups(ExprWithCleanups *E); void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E); void VisitCXXTypeidExpr(CXXTypeidExpr *E) { EmitAggLoadOfLValue(E); } - + void VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E); void VisitOpaqueValueExpr(OpaqueValueExpr *E); void VisitVAArgExpr(VAArgExpr *E); - void EmitInitializationToLValue(Expr *E, LValue Address, QualType T); - void EmitNullInitializationToLValue(LValue Address, QualType T); + void EmitInitializationToLValue(Expr *E, LValue Address); + void EmitNullInitializationToLValue(LValue Address); // case Expr::ChooseExprClass: void VisitCXXThrowExpr(const CXXThrowExpr *E) { CGF.EmitCXXThrowExpr(E); } }; @@ -243,10 +244,31 @@ void AggExprEmitter::EmitFinalDestCopy(const Expr *E, LValue Src, bool Ignore) { // Visitor Methods //===----------------------------------------------------------------------===// +void AggExprEmitter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E){ + Visit(E->GetTemporaryExpr()); +} + void AggExprEmitter::VisitOpaqueValueExpr(OpaqueValueExpr *e) { EmitFinalDestCopy(e, CGF.getOpaqueLValueMapping(e)); } +void +AggExprEmitter::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { + if (E->getType().isPODType(CGF.getContext())) { + // For a POD type, just emit a load of the lvalue + a copy, because our + // compound literal might alias the destination. + // FIXME: This is a band-aid; the real problem appears to be in our handling + // of assignments, where we store directly into the LHS without checking + // whether anything in the RHS aliases. + EmitAggLoadOfLValue(E); + return; + } + + AggValueSlot Slot = EnsureSlot(E->getType()); + CGF.EmitAggExpr(E->getInitializer(), Slot); +} + + void AggExprEmitter::VisitCastExpr(CastExpr *E) { switch (E->getCastKind()) { case CK_Dynamic: { @@ -271,8 +293,8 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { QualType PtrTy = CGF.getContext().getPointerType(Ty); llvm::Value *CastPtr = Builder.CreateBitCast(Dest.getAddr(), CGF.ConvertType(PtrTy)); - EmitInitializationToLValue(E->getSubExpr(), CGF.MakeAddrLValue(CastPtr, Ty), - Ty); + EmitInitializationToLValue(E->getSubExpr(), + CGF.MakeAddrLValue(CastPtr, Ty)); break; } @@ -339,6 +361,9 @@ void AggExprEmitter::VisitCastExpr(CastExpr *E) { case CK_IntegralComplexToBoolean: case CK_IntegralComplexCast: case CK_IntegralComplexToFloatingComplex: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: llvm_unreachable("cast kind invalid for aggregate types"); } } @@ -519,13 +544,13 @@ void AggExprEmitter::VisitExprWithCleanups(ExprWithCleanups *E) { void AggExprEmitter::VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E) { QualType T = E->getType(); AggValueSlot Slot = EnsureSlot(T); - EmitNullInitializationToLValue(CGF.MakeAddrLValue(Slot.getAddr(), T), T); + EmitNullInitializationToLValue(CGF.MakeAddrLValue(Slot.getAddr(), T)); } void AggExprEmitter::VisitImplicitValueInitExpr(ImplicitValueInitExpr *E) { QualType T = E->getType(); AggValueSlot Slot = EnsureSlot(T); - EmitNullInitializationToLValue(CGF.MakeAddrLValue(Slot.getAddr(), T), T); + EmitNullInitializationToLValue(CGF.MakeAddrLValue(Slot.getAddr(), T)); } /// isSimpleZero - If emitting this value will obviously just cause a store of @@ -557,41 +582,46 @@ static bool isSimpleZero(const Expr *E, CodeGenFunction &CGF) { void -AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV, QualType T) { +AggExprEmitter::EmitInitializationToLValue(Expr* E, LValue LV) { + QualType type = LV.getType(); // FIXME: Ignore result? // FIXME: Are initializers affected by volatile? if (Dest.isZeroed() && isSimpleZero(E, CGF)) { // Storing "i32 0" to a zero'd memory location is a noop. } else if (isa<ImplicitValueInitExpr>(E)) { - EmitNullInitializationToLValue(LV, T); - } else if (T->isReferenceType()) { + EmitNullInitializationToLValue(LV); + } else if (type->isReferenceType()) { RValue RV = CGF.EmitReferenceBindingToExpr(E, /*InitializedDecl=*/0); - CGF.EmitStoreThroughLValue(RV, LV, T); - } else if (T->isAnyComplexType()) { + CGF.EmitStoreThroughLValue(RV, LV); + } else if (type->isAnyComplexType()) { CGF.EmitComplexExprIntoAddr(E, LV.getAddress(), false); - } else if (CGF.hasAggregateLLVMType(T)) { - CGF.EmitAggExpr(E, AggValueSlot::forAddr(LV.getAddress(), false, true, - false, Dest.isZeroed())); + } else if (CGF.hasAggregateLLVMType(type)) { + CGF.EmitAggExpr(E, AggValueSlot::forLValue(LV, true, false, + Dest.isZeroed())); + } else if (LV.isSimple()) { + CGF.EmitScalarInit(E, /*D=*/0, LV, /*Captured=*/false); } else { - CGF.EmitStoreThroughLValue(RValue::get(CGF.EmitScalarExpr(E)), LV, T); + CGF.EmitStoreThroughLValue(RValue::get(CGF.EmitScalarExpr(E)), LV); } } -void AggExprEmitter::EmitNullInitializationToLValue(LValue LV, QualType T) { +void AggExprEmitter::EmitNullInitializationToLValue(LValue lv) { + QualType type = lv.getType(); + // If the destination slot is already zeroed out before the aggregate is // copied into it, we don't have to emit any zeros here. - if (Dest.isZeroed() && CGF.getTypes().isZeroInitializable(T)) + if (Dest.isZeroed() && CGF.getTypes().isZeroInitializable(type)) return; - if (!CGF.hasAggregateLLVMType(T)) { + if (!CGF.hasAggregateLLVMType(type)) { // For non-aggregates, we can store zero - llvm::Value *Null = llvm::Constant::getNullValue(CGF.ConvertType(T)); - CGF.EmitStoreThroughLValue(RValue::get(Null), LV, T); + llvm::Value *null = llvm::Constant::getNullValue(CGF.ConvertType(type)); + CGF.EmitStoreThroughLValue(RValue::get(null), lv); } else { // There's a potential optimization opportunity in combining // memsets; that would be easy for arrays, but relatively // difficult for structures with the current code. - CGF.EmitNullInitialization(LV.getAddress(), T); + CGF.EmitNullInitialization(lv.getAddress(), lv.getType()); } } @@ -634,45 +664,135 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { } uint64_t NumArrayElements = AType->getNumElements(); - QualType ElementType = CGF.getContext().getCanonicalType(E->getType()); - ElementType = CGF.getContext().getAsArrayType(ElementType)->getElementType(); - - bool hasNonTrivialCXXConstructor = false; - if (CGF.getContext().getLangOptions().CPlusPlus) - if (const RecordType *RT = CGF.getContext() - .getBaseElementType(ElementType)->getAs<RecordType>()) { - const CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); - hasNonTrivialCXXConstructor = !RD->hasTrivialDefaultConstructor(); + assert(NumInitElements <= NumArrayElements); + + QualType elementType = E->getType().getCanonicalType(); + elementType = CGF.getContext().getQualifiedType( + cast<ArrayType>(elementType)->getElementType(), + elementType.getQualifiers() + Dest.getQualifiers()); + + // DestPtr is an array*. Construct an elementType* by drilling + // down a level. + llvm::Value *zero = llvm::ConstantInt::get(CGF.SizeTy, 0); + llvm::Value *indices[] = { zero, zero }; + llvm::Value *begin = + Builder.CreateInBoundsGEP(DestPtr, indices, indices+2, "arrayinit.begin"); + + // Exception safety requires us to destroy all the + // already-constructed members if an initializer throws. + // For that, we'll need an EH cleanup. + QualType::DestructionKind dtorKind = elementType.isDestructedType(); + llvm::AllocaInst *endOfInit = 0; + EHScopeStack::stable_iterator cleanup; + if (CGF.needsEHCleanup(dtorKind)) { + // In principle we could tell the cleanup where we are more + // directly, but the control flow can get so varied here that it + // would actually be quite complex. Therefore we go through an + // alloca. + endOfInit = CGF.CreateTempAlloca(begin->getType(), + "arrayinit.endOfInit"); + Builder.CreateStore(begin, endOfInit); + CGF.pushIrregularPartialArrayCleanup(begin, endOfInit, elementType, + CGF.getDestroyer(dtorKind)); + cleanup = CGF.EHStack.stable_begin(); + + // Otherwise, remember that we didn't need a cleanup. + } else { + dtorKind = QualType::DK_none; + } + + llvm::Value *one = llvm::ConstantInt::get(CGF.SizeTy, 1); + + // The 'current element to initialize'. The invariants on this + // variable are complicated. Essentially, after each iteration of + // the loop, it points to the last initialized element, except + // that it points to the beginning of the array before any + // elements have been initialized. + llvm::Value *element = begin; + + // Emit the explicit initializers. + for (uint64_t i = 0; i != NumInitElements; ++i) { + // Advance to the next element. + if (i > 0) { + element = Builder.CreateInBoundsGEP(element, one, "arrayinit.element"); + + // Tell the cleanup that it needs to destroy up to this + // element. TODO: some of these stores can be trivially + // observed to be unnecessary. + if (endOfInit) Builder.CreateStore(element, endOfInit); } - // FIXME: were we intentionally ignoring address spaces and GC attributes? + LValue elementLV = CGF.MakeAddrLValue(element, elementType); + EmitInitializationToLValue(E->getInit(i), elementLV); + } - for (uint64_t i = 0; i != NumArrayElements; ++i) { - // If we're done emitting initializers and the destination is known-zeroed - // then we're done. - if (i == NumInitElements && - Dest.isZeroed() && - CGF.getTypes().isZeroInitializable(ElementType) && - !hasNonTrivialCXXConstructor) - break; + // Check whether there's a non-trivial array-fill expression. + // Note that this will be a CXXConstructExpr even if the element + // type is an array (or array of array, etc.) of class type. + Expr *filler = E->getArrayFiller(); + bool hasTrivialFiller = true; + if (CXXConstructExpr *cons = dyn_cast_or_null<CXXConstructExpr>(filler)) { + assert(cons->getConstructor()->isDefaultConstructor()); + hasTrivialFiller = cons->getConstructor()->isTrivial(); + } - llvm::Value *NextVal = Builder.CreateStructGEP(DestPtr, i, ".array"); - LValue LV = CGF.MakeAddrLValue(NextVal, ElementType); - - if (i < NumInitElements) - EmitInitializationToLValue(E->getInit(i), LV, ElementType); - else if (Expr *filler = E->getArrayFiller()) - EmitInitializationToLValue(filler, LV, ElementType); + // Any remaining elements need to be zero-initialized, possibly + // using the filler expression. We can skip this if the we're + // emitting to zeroed memory. + if (NumInitElements != NumArrayElements && + !(Dest.isZeroed() && hasTrivialFiller && + CGF.getTypes().isZeroInitializable(elementType))) { + + // Use an actual loop. This is basically + // do { *array++ = filler; } while (array != end); + + // Advance to the start of the rest of the array. + if (NumInitElements) { + element = Builder.CreateInBoundsGEP(element, one, "arrayinit.start"); + if (endOfInit) Builder.CreateStore(element, endOfInit); + } + + // Compute the end of the array. + llvm::Value *end = Builder.CreateInBoundsGEP(begin, + llvm::ConstantInt::get(CGF.SizeTy, NumArrayElements), + "arrayinit.end"); + + llvm::BasicBlock *entryBB = Builder.GetInsertBlock(); + llvm::BasicBlock *bodyBB = CGF.createBasicBlock("arrayinit.body"); + + // Jump into the body. + CGF.EmitBlock(bodyBB); + llvm::PHINode *currentElement = + Builder.CreatePHI(element->getType(), 2, "arrayinit.cur"); + currentElement->addIncoming(element, entryBB); + + // Emit the actual filler expression. + LValue elementLV = CGF.MakeAddrLValue(currentElement, elementType); + if (filler) + EmitInitializationToLValue(filler, elementLV); else - EmitNullInitializationToLValue(LV, ElementType); - - // If the GEP didn't get used because of a dead zero init or something - // else, clean it up for -O0 builds and general tidiness. - if (llvm::GetElementPtrInst *GEP = - dyn_cast<llvm::GetElementPtrInst>(NextVal)) - if (GEP->use_empty()) - GEP->eraseFromParent(); + EmitNullInitializationToLValue(elementLV); + + // Move on to the next element. + llvm::Value *nextElement = + Builder.CreateInBoundsGEP(currentElement, one, "arrayinit.next"); + + // Tell the EH cleanup that we finished with the last element. + if (endOfInit) Builder.CreateStore(nextElement, endOfInit); + + // Leave the loop if we're done. + llvm::Value *done = Builder.CreateICmpEQ(nextElement, end, + "arrayinit.done"); + llvm::BasicBlock *endBB = CGF.createBasicBlock("arrayinit.end"); + Builder.CreateCondBr(done, endBB, bodyBB); + currentElement->addIncoming(nextElement, Builder.GetInsertBlock()); + + CGF.EmitBlock(endBB); } + + // Leave the partial-array cleanup if we entered one. + if (dtorKind) CGF.DeactivateCleanupBlock(cleanup); + return; } @@ -683,9 +803,9 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { // the disadvantage is that the generated code is more difficult for // the optimizer, especially with bitfields. unsigned NumInitElements = E->getNumInits(); - RecordDecl *SD = E->getType()->getAs<RecordType>()->getDecl(); + RecordDecl *record = E->getType()->castAs<RecordType>()->getDecl(); - if (E->getType()->isUnionType()) { + if (record->isUnion()) { // Only initialize one field of a union. The field itself is // specified by the initializer list. if (!E->getInitializedFieldInUnion()) { @@ -694,8 +814,8 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { #ifndef NDEBUG // Make sure that it's really an empty and not a failure of // semantic analysis. - for (RecordDecl::field_iterator Field = SD->field_begin(), - FieldEnd = SD->field_end(); + for (RecordDecl::field_iterator Field = record->field_begin(), + FieldEnd = record->field_end(); Field != FieldEnd; ++Field) assert(Field->isUnnamedBitfield() && "Only unnamed bitfields allowed"); #endif @@ -708,55 +828,81 @@ void AggExprEmitter::VisitInitListExpr(InitListExpr *E) { LValue FieldLoc = CGF.EmitLValueForFieldInitialization(DestPtr, Field, 0); if (NumInitElements) { // Store the initializer into the field - EmitInitializationToLValue(E->getInit(0), FieldLoc, Field->getType()); + EmitInitializationToLValue(E->getInit(0), FieldLoc); } else { // Default-initialize to null. - EmitNullInitializationToLValue(FieldLoc, Field->getType()); + EmitNullInitializationToLValue(FieldLoc); } return; } + // We'll need to enter cleanup scopes in case any of the member + // initializers throw an exception. + llvm::SmallVector<EHScopeStack::stable_iterator, 16> cleanups; + // Here we iterate over the fields; this makes it simpler to both // default-initialize fields and skip over unnamed fields. - unsigned CurInitVal = 0; - for (RecordDecl::field_iterator Field = SD->field_begin(), - FieldEnd = SD->field_end(); - Field != FieldEnd; ++Field) { - // We're done once we hit the flexible array member - if (Field->getType()->isIncompleteArrayType()) + unsigned curInitIndex = 0; + for (RecordDecl::field_iterator field = record->field_begin(), + fieldEnd = record->field_end(); + field != fieldEnd; ++field) { + // We're done once we hit the flexible array member. + if (field->getType()->isIncompleteArrayType()) break; - if (Field->isUnnamedBitfield()) + // Always skip anonymous bitfields. + if (field->isUnnamedBitfield()) continue; - // Don't emit GEP before a noop store of zero. - if (CurInitVal == NumInitElements && Dest.isZeroed() && + // We're done if we reach the end of the explicit initializers, we + // have a zeroed object, and the rest of the fields are + // zero-initializable. + if (curInitIndex == NumInitElements && Dest.isZeroed() && CGF.getTypes().isZeroInitializable(E->getType())) break; // FIXME: volatility - LValue FieldLoc = CGF.EmitLValueForFieldInitialization(DestPtr, *Field, 0); + LValue LV = CGF.EmitLValueForFieldInitialization(DestPtr, *field, 0); // We never generate write-barries for initialized fields. - FieldLoc.setNonGC(true); + LV.setNonGC(true); - if (CurInitVal < NumInitElements) { + if (curInitIndex < NumInitElements) { // Store the initializer into the field. - EmitInitializationToLValue(E->getInit(CurInitVal++), FieldLoc, - Field->getType()); + EmitInitializationToLValue(E->getInit(curInitIndex++), LV); } else { // We're out of initalizers; default-initialize to null - EmitNullInitializationToLValue(FieldLoc, Field->getType()); + EmitNullInitializationToLValue(LV); + } + + // Push a destructor if necessary. + // FIXME: if we have an array of structures, all explicitly + // initialized, we can end up pushing a linear number of cleanups. + bool pushedCleanup = false; + if (QualType::DestructionKind dtorKind + = field->getType().isDestructedType()) { + assert(LV.isSimple()); + if (CGF.needsEHCleanup(dtorKind)) { + CGF.pushDestroy(EHCleanup, LV.getAddress(), field->getType(), + CGF.getDestroyer(dtorKind), false); + cleanups.push_back(CGF.EHStack.stable_begin()); + pushedCleanup = true; + } } // If the GEP didn't get used because of a dead zero init or something // else, clean it up for -O0 builds and general tidiness. - if (FieldLoc.isSimple()) + if (!pushedCleanup && LV.isSimple()) if (llvm::GetElementPtrInst *GEP = - dyn_cast<llvm::GetElementPtrInst>(FieldLoc.getAddress())) + dyn_cast<llvm::GetElementPtrInst>(LV.getAddress())) if (GEP->use_empty()) GEP->eraseFromParent(); } + + // Deactivate all the partial cleanups in reverse order, which + // generally means popping them. + for (unsigned i = cleanups.size(); i != 0; --i) + CGF.DeactivateCleanupBlock(cleanups[i-1]); } //===----------------------------------------------------------------------===// @@ -873,8 +1019,6 @@ static void CheckAggExprForMemSetUse(AggValueSlot &Slot, const Expr *E, /// /// \param IsInitializer - true if this evaluation is initializing an /// object whose lifetime is already being managed. -// -// FIXME: Take Qualifiers object. void CodeGenFunction::EmitAggExpr(const Expr *E, AggValueSlot Slot, bool IgnoreResult) { assert(E && hasAggregateLLVMType(E->getType()) && @@ -892,7 +1036,7 @@ LValue CodeGenFunction::EmitAggExprToLValue(const Expr *E) { assert(hasAggregateLLVMType(E->getType()) && "Invalid argument!"); llvm::Value *Temp = CreateMemTemp(E->getType()); LValue LV = MakeAddrLValue(Temp, E->getType()); - EmitAggExpr(E, AggValueSlot::forAddr(Temp, LV.isVolatileQualified(), false)); + EmitAggExpr(E, AggValueSlot::forLValue(LV, false)); return LV; } @@ -954,7 +1098,10 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, llvm::Type::getInt8PtrTy(getLLVMContext(), SPT->getAddressSpace()); SrcPtr = Builder.CreateBitCast(SrcPtr, SBP, "tmp"); - if (const RecordType *RecordTy = Ty->getAs<RecordType>()) { + // Don't do any of the memmove_collectable tests if GC isn't set. + if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) { + // fall through + } else if (const RecordType *RecordTy = Ty->getAs<RecordType>()) { RecordDecl *Record = RecordTy->getDecl(); if (Record->hasObjectMember()) { CharUnits size = TypeInfo.first; @@ -964,7 +1111,7 @@ void CodeGenFunction::EmitAggregateCopy(llvm::Value *DestPtr, SizeVal); return; } - } else if (getContext().getAsArrayType(Ty)) { + } else if (Ty->isArrayType()) { QualType BaseType = getContext().getBaseElementType(Ty); if (const RecordType *RecordTy = BaseType->getAs<RecordType>()) { if (RecordTy->getDecl()->hasObjectMember()) { diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index 81fee677f61a..4396f567f2f9 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -379,19 +379,11 @@ CodeGenFunction::EmitCXXConstructExpr(const CXXConstructExpr *E, } } - const ConstantArrayType *Array - = getContext().getAsConstantArrayType(E->getType()); - if (Array) { - QualType BaseElementTy = getContext().getBaseElementType(Array); - const llvm::Type *BasePtr = ConvertType(BaseElementTy); - BasePtr = llvm::PointerType::getUnqual(BasePtr); - llvm::Value *BaseAddrPtr = - Builder.CreateBitCast(Dest.getAddr(), BasePtr); - - EmitCXXAggrConstructorCall(CD, Array, BaseAddrPtr, + if (const ConstantArrayType *arrayType + = getContext().getAsConstantArrayType(E->getType())) { + EmitCXXAggrConstructorCall(CD, arrayType, Dest.getAddr(), E->arg_begin(), E->arg_end()); - } - else { + } else { CXXCtorType Type = Ctor_Complete; bool ForVirtualBase = false; @@ -619,10 +611,8 @@ static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF, // can be ignored because the result shouldn't be used if // allocation fails. if (typeSizeMultiplier != 1) { - const llvm::Type *intrinsicTypes[] = { CGF.SizeTy }; llvm::Value *umul_with_overflow - = CGF.CGM.getIntrinsic(llvm::Intrinsic::umul_with_overflow, - intrinsicTypes, 1); + = CGF.CGM.getIntrinsic(llvm::Intrinsic::umul_with_overflow, CGF.SizeTy); llvm::Value *tsmV = llvm::ConstantInt::get(CGF.SizeTy, typeSizeMultiplier); @@ -661,10 +651,8 @@ static llvm::Value *EmitCXXNewAllocSize(CodeGenFunction &CGF, if (cookieSize != 0) { sizeWithoutCookie = size; - const llvm::Type *intrinsicTypes[] = { CGF.SizeTy }; llvm::Value *uadd_with_overflow - = CGF.CGM.getIntrinsic(llvm::Intrinsic::uadd_with_overflow, - intrinsicTypes, 1); + = CGF.CGM.getIntrinsic(llvm::Intrinsic::uadd_with_overflow, CGF.SizeTy); llvm::Value *cookieSizeV = llvm::ConstantInt::get(CGF.SizeTy, cookieSize); llvm::Value *result = @@ -707,16 +695,15 @@ static void StoreAnyExprIntoOneUnit(CodeGenFunction &CGF, const CXXNewExpr *E, unsigned Alignment = CGF.getContext().getTypeAlignInChars(AllocType).getQuantity(); - if (!CGF.hasAggregateLLVMType(AllocType)) - CGF.EmitStoreOfScalar(CGF.EmitScalarExpr(Init), NewPtr, - AllocType.isVolatileQualified(), Alignment, - AllocType); + if (!CGF.hasAggregateLLVMType(AllocType)) + CGF.EmitScalarInit(Init, 0, CGF.MakeAddrLValue(NewPtr, AllocType, Alignment), + false); else if (AllocType->isAnyComplexType()) CGF.EmitComplexExprIntoAddr(Init, NewPtr, AllocType.isVolatileQualified()); else { AggValueSlot Slot - = AggValueSlot::forAddr(NewPtr, AllocType.isVolatileQualified(), true); + = AggValueSlot::forAddr(NewPtr, AllocType.getQualifiers(), true); CGF.EmitAggExpr(Init, Slot); } } @@ -806,7 +793,7 @@ static void EmitNewInitializer(CodeGenFunction &CGF, const CXXNewExpr *E, RequiresZeroInitialization = true; } - + CGF.EmitCXXAggrConstructorCall(Ctor, NumElements, NewPtr, E->constructor_arg_begin(), E->constructor_arg_end(), @@ -875,7 +862,7 @@ namespace { getPlacementArgs()[I] = Arg; } - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { const FunctionProtoType *FPT = OperatorDelete->getType()->getAs<FunctionProtoType>(); assert(FPT->getNumArgs() == NumPlacementArgs + 1 || @@ -932,7 +919,7 @@ namespace { getPlacementArgs()[I] = Arg; } - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { const FunctionProtoType *FPT = OperatorDelete->getType()->getAs<FunctionProtoType>(); assert(FPT->getNumArgs() == NumPlacementArgs + 1 || @@ -1075,7 +1062,7 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { // CXXNewExpr::shouldNullCheckAllocation()) and we have an // interesting initializer. bool nullCheck = allocatorType->isNothrow(getContext()) && - !(allocType->isPODType() && !E->hasInitializer()); + !(allocType.isPODType(getContext()) && !E->hasInitializer()); llvm::BasicBlock *nullCheckBB = 0; llvm::BasicBlock *contBB = 0; @@ -1202,7 +1189,7 @@ namespace { QualType ElementType) : Ptr(Ptr), OperatorDelete(OperatorDelete), ElementType(ElementType) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.EmitDeleteCall(OperatorDelete, Ptr, ElementType); } }; @@ -1212,7 +1199,8 @@ namespace { static void EmitObjectDelete(CodeGenFunction &CGF, const FunctionDecl *OperatorDelete, llvm::Value *Ptr, - QualType ElementType) { + QualType ElementType, + bool UseGlobalDelete) { // Find the destructor for the type, if applicable. If the // destructor is virtual, we'll just emit the vcall and return. const CXXDestructorDecl *Dtor = 0; @@ -1222,17 +1210,30 @@ static void EmitObjectDelete(CodeGenFunction &CGF, Dtor = RD->getDestructor(); if (Dtor->isVirtual()) { + if (UseGlobalDelete) { + // If we're supposed to call the global delete, make sure we do so + // even if the destructor throws. + CGF.EHStack.pushCleanup<CallObjectDelete>(NormalAndEHCleanup, + Ptr, OperatorDelete, + ElementType); + } + const llvm::Type *Ty = CGF.getTypes().GetFunctionType(CGF.getTypes().getFunctionInfo(Dtor, Dtor_Complete), /*isVariadic=*/false); llvm::Value *Callee - = CGF.BuildVirtualCall(Dtor, Dtor_Deleting, Ptr, Ty); + = CGF.BuildVirtualCall(Dtor, + UseGlobalDelete? Dtor_Complete : Dtor_Deleting, + Ptr, Ty); CGF.EmitCXXMemberCall(Dtor, Callee, ReturnValueSlot(), Ptr, /*VTT=*/0, 0, 0); - // The dtor took care of deleting the object. + if (UseGlobalDelete) { + CGF.PopCleanupBlock(); + } + return; } } @@ -1247,7 +1248,29 @@ static void EmitObjectDelete(CodeGenFunction &CGF, if (Dtor) CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, Ptr); + else if (CGF.getLangOptions().ObjCAutoRefCount && + ElementType->isObjCLifetimeType()) { + switch (ElementType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + case Qualifiers::OCL_Strong: { + // Load the pointer value. + llvm::Value *PtrValue = CGF.Builder.CreateLoad(Ptr, + ElementType.isVolatileQualified()); + + CGF.EmitARCRelease(PtrValue, /*precise*/ true); + break; + } + + case Qualifiers::OCL_Weak: + CGF.EmitARCDestroyWeak(Ptr); + break; + } + } + CGF.PopCleanupBlock(); } @@ -1268,7 +1291,7 @@ namespace { : Ptr(Ptr), OperatorDelete(OperatorDelete), NumElements(NumElements), ElementType(ElementType), CookieSize(CookieSize) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { const FunctionProtoType *DeleteFTy = OperatorDelete->getType()->getAs<FunctionProtoType>(); assert(DeleteFTy->getNumArgs() == 1 || DeleteFTy->getNumArgs() == 2); @@ -1316,31 +1339,40 @@ namespace { /// Emit the code for deleting an array of objects. static void EmitArrayDelete(CodeGenFunction &CGF, const CXXDeleteExpr *E, - llvm::Value *Ptr, - QualType ElementType) { - llvm::Value *NumElements = 0; - llvm::Value *AllocatedPtr = 0; - CharUnits CookieSize; - CGF.CGM.getCXXABI().ReadArrayCookie(CGF, Ptr, E, ElementType, - NumElements, AllocatedPtr, CookieSize); + llvm::Value *deletedPtr, + QualType elementType) { + llvm::Value *numElements = 0; + llvm::Value *allocatedPtr = 0; + CharUnits cookieSize; + CGF.CGM.getCXXABI().ReadArrayCookie(CGF, deletedPtr, E, elementType, + numElements, allocatedPtr, cookieSize); - assert(AllocatedPtr && "ReadArrayCookie didn't set AllocatedPtr"); + assert(allocatedPtr && "ReadArrayCookie didn't set allocated pointer"); // Make sure that we call delete even if one of the dtors throws. - const FunctionDecl *OperatorDelete = E->getOperatorDelete(); + const FunctionDecl *operatorDelete = E->getOperatorDelete(); CGF.EHStack.pushCleanup<CallArrayDelete>(NormalAndEHCleanup, - AllocatedPtr, OperatorDelete, - NumElements, ElementType, - CookieSize); - - if (const CXXRecordDecl *RD = ElementType->getAsCXXRecordDecl()) { - if (!RD->hasTrivialDestructor()) { - assert(NumElements && "ReadArrayCookie didn't find element count" - " for a class with destructor"); - CGF.EmitCXXAggrDestructorCall(RD->getDestructor(), NumElements, Ptr); - } + allocatedPtr, operatorDelete, + numElements, elementType, + cookieSize); + + // Destroy the elements. + if (QualType::DestructionKind dtorKind = elementType.isDestructedType()) { + assert(numElements && "no element count for a type with a destructor!"); + + llvm::Value *arrayEnd = + CGF.Builder.CreateInBoundsGEP(deletedPtr, numElements, "delete.end"); + + // Note that it is legal to allocate a zero-length array, and we + // can never fold the check away because the length should always + // come from a cookie. + CGF.emitArrayDestroy(deletedPtr, arrayEnd, elementType, + CGF.getDestroyer(dtorKind), + /*checkZeroLength*/ true, + CGF.needsEHCleanup(dtorKind)); } + // Pop the cleanup block. CGF.PopCleanupBlock(); } @@ -1397,7 +1429,8 @@ void CodeGenFunction::EmitCXXDeleteExpr(const CXXDeleteExpr *E) { if (E->isArrayForm()) { EmitArrayDelete(*this, E, Ptr, DeleteTy); } else { - EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy); + EmitObjectDelete(*this, E->getOperatorDelete(), Ptr, DeleteTy, + E->isGlobalDelete()); } EmitBlock(DeleteEnd); @@ -1415,7 +1448,7 @@ static llvm::Constant *getBadTypeidFn(CodeGenFunction &CGF) { static void EmitBadTypeidCall(CodeGenFunction &CGF) { llvm::Value *Fn = getBadTypeidFn(CGF); - CGF.EmitCallOrInvoke(Fn, 0, 0).setDoesNotReturn(); + CGF.EmitCallOrInvoke(Fn).setDoesNotReturn(); CGF.Builder.CreateUnreachable(); } @@ -1489,11 +1522,11 @@ static llvm::Constant *getDynamicCastFn(CodeGenFunction &CGF) { // const abi::__class_type_info *dst, // std::ptrdiff_t src2dst_offset); - const llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); - const llvm::Type *PtrDiffTy = + llvm::Type *Int8PtrTy = llvm::Type::getInt8PtrTy(CGF.getLLVMContext()); + llvm::Type *PtrDiffTy = CGF.ConvertType(CGF.getContext().getPointerDiffType()); - const llvm::Type *Args[4] = { Int8PtrTy, Int8PtrTy, Int8PtrTy, PtrDiffTy }; + llvm::Type *Args[4] = { Int8PtrTy, Int8PtrTy, Int8PtrTy, PtrDiffTy }; const llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, Args, false); @@ -1513,7 +1546,7 @@ static llvm::Constant *getBadCastFn(CodeGenFunction &CGF) { static void EmitBadCastCall(CodeGenFunction &CGF) { llvm::Value *Fn = getBadCastFn(CGF); - CGF.EmitCallOrInvoke(Fn, 0, 0).setDoesNotReturn(); + CGF.EmitCallOrInvoke(Fn).setDoesNotReturn(); CGF.Builder.CreateUnreachable(); } diff --git a/lib/CodeGen/CGExprComplex.cpp b/lib/CodeGen/CGExprComplex.cpp index bd1958602484..35cff1d72714 100644 --- a/lib/CodeGen/CGExprComplex.cpp +++ b/lib/CodeGen/CGExprComplex.cpp @@ -112,6 +112,10 @@ public: return Visit(GE->getResultExpr()); } ComplexPairTy VisitImaginaryLiteral(const ImaginaryLiteral *IL); + ComplexPairTy + VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *PE) { + return Visit(PE->getReplacement()); + } // l-values. ComplexPairTy VisitDeclRefExpr(const Expr *E) { return EmitLoadOfLValue(E); } @@ -352,6 +356,8 @@ ComplexPairTy ComplexExprEmitter::EmitComplexToComplexCast(ComplexPairTy Val, ComplexPairTy ComplexExprEmitter::EmitCast(CastExpr::CastKind CK, Expr *Op, QualType DestTy) { switch (CK) { + case CK_Dependent: llvm_unreachable("dependent cast kind in IR gen!"); + case CK_GetObjCProperty: { LValue LV = CGF.EmitLValue(Op); assert(LV.isPropertyRef() && "Unknown LValue type!"); @@ -360,39 +366,74 @@ ComplexPairTy ComplexExprEmitter::EmitCast(CastExpr::CastKind CK, Expr *Op, case CK_NoOp: case CK_LValueToRValue: + case CK_UserDefinedConversion: return Visit(Op); - // TODO: do all of these - default: - break; - } - - // Two cases here: cast from (complex to complex) and (scalar to complex). - if (Op->getType()->isAnyComplexType()) - return EmitComplexToComplexCast(Visit(Op), Op->getType(), DestTy); - - // FIXME: We should be looking at all of the cast kinds here, not - // cherry-picking the ones we have test cases for. - if (CK == CK_LValueBitCast) { + case CK_LValueBitCast: { llvm::Value *V = CGF.EmitLValue(Op).getAddress(); V = Builder.CreateBitCast(V, - CGF.ConvertType(CGF.getContext().getPointerType(DestTy))); + CGF.ConvertType(CGF.getContext().getPointerType(DestTy))); // FIXME: Are the qualifiers correct here? return EmitLoadOfComplex(V, DestTy.isVolatileQualified()); } - - // C99 6.3.1.7: When a value of real type is converted to a complex type, the - // real part of the complex result value is determined by the rules of - // conversion to the corresponding real type and the imaginary part of the - // complex result value is a positive zero or an unsigned zero. - llvm::Value *Elt = CGF.EmitScalarExpr(Op); - - // Convert the input element to the element type of the complex. - DestTy = DestTy->getAs<ComplexType>()->getElementType(); - Elt = CGF.EmitScalarConversion(Elt, Op->getType(), DestTy); - - // Return (realval, 0). - return ComplexPairTy(Elt, llvm::Constant::getNullValue(Elt->getType())); + + case CK_BitCast: + case CK_BaseToDerived: + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + case CK_Dynamic: + case CK_ToUnion: + case CK_ArrayToPointerDecay: + case CK_FunctionToPointerDecay: + case CK_NullToPointer: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_MemberPointerToBoolean: + case CK_ConstructorConversion: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_PointerToBoolean: + case CK_ToVoid: + case CK_VectorSplat: + case CK_IntegralCast: + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: + llvm_unreachable("invalid cast kind for complex value"); + + case CK_FloatingRealToComplex: + case CK_IntegralRealToComplex: { + llvm::Value *Elt = CGF.EmitScalarExpr(Op); + + // Convert the input element to the element type of the complex. + DestTy = DestTy->getAs<ComplexType>()->getElementType(); + Elt = CGF.EmitScalarConversion(Elt, Op->getType(), DestTy); + + // Return (realval, 0). + return ComplexPairTy(Elt, llvm::Constant::getNullValue(Elt->getType())); + } + + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + return EmitComplexToComplexCast(Visit(Op), Op->getType(), DestTy); + } + + llvm_unreachable("unknown cast resulting in complex value"); } ComplexPairTy ComplexExprEmitter::VisitUnaryMinus(const UnaryOperator *E) { diff --git a/lib/CodeGen/CGExprConstant.cpp b/lib/CodeGen/CGExprConstant.cpp index da37bd5b0a1f..45e44dda0f58 100644 --- a/lib/CodeGen/CGExprConstant.cpp +++ b/lib/CodeGen/CGExprConstant.cpp @@ -433,9 +433,19 @@ llvm::Constant *ConstStructBuilder:: if (!Builder.Build(ILE)) return 0; + // Pick the type to use. If the type is layout identical to the ConvertType + // type then use it, otherwise use whatever the builder produced for us. + const llvm::StructType *STy = + llvm::ConstantStruct::getTypeForElements(CGM.getLLVMContext(), + Builder.Elements,Builder.Packed); + const llvm::Type *ILETy = CGM.getTypes().ConvertType(ILE->getType()); + if (const llvm::StructType *ILESTy = dyn_cast<llvm::StructType>(ILETy)) { + if (ILESTy->isLayoutIdentical(STy)) + STy = ILESTy; + } + llvm::Constant *Result = - llvm::ConstantStruct::get(CGM.getLLVMContext(), - Builder.Elements, Builder.Packed); + llvm::ConstantStruct::get(STy, Builder.Elements); assert(Builder.NextFieldOffsetInChars.RoundUpToAlignment( Builder.getAlignment(Result)) == @@ -471,6 +481,11 @@ public: return Visit(PE->getSubExpr()); } + llvm::Constant * + VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *PE) { + return Visit(PE->getReplacement()); + } + llvm::Constant *VisitGenericSelectionExpr(GenericSelectionExpr *GE) { return Visit(GE->getResultExpr()); } @@ -523,7 +538,7 @@ public: // Build a struct with the union sub-element as the first member, // and padded to the appropriate size std::vector<llvm::Constant*> Elts; - std::vector<const llvm::Type*> Types; + std::vector<llvm::Type*> Types; Elts.push_back(C); Types.push_back(C->getType()); unsigned CurSize = CGM.getTargetData().getTypeAllocSize(C->getType()); @@ -531,7 +546,7 @@ public: assert(CurSize <= TotalSize && "Union size mismatch!"); if (unsigned NumPadBytes = TotalSize - CurSize) { - const llvm::Type *Ty = llvm::Type::getInt8Ty(VMContext); + llvm::Type *Ty = llvm::Type::getInt8Ty(VMContext); if (NumPadBytes > 1) Ty = llvm::ArrayType::get(Ty, NumPadBytes); @@ -570,6 +585,9 @@ public: case CK_GetObjCProperty: case CK_ToVoid: case CK_Dynamic: + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: return 0; // These might need to be supported for constexpr. @@ -650,6 +668,10 @@ public: return Visit(DAE->getExpr()); } + llvm::Constant *VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { + return Visit(E->GetTemporaryExpr()); + } + llvm::Constant *EmitArrayInitialization(InitListExpr *ILE) { unsigned NumInitElements = ILE->getNumInits(); if (NumInitElements == 1 && ILE->getType() == ILE->getInit(0)->getType() && @@ -694,7 +716,7 @@ public: if (RewriteType) { // FIXME: Try to avoid packing the array - std::vector<const llvm::Type*> Types; + std::vector<llvm::Type*> Types; for (unsigned i = 0; i < Elts.size(); ++i) Types.push_back(Elts[i]->getType()); const llvm::StructType *SType = llvm::StructType::get(AType->getContext(), @@ -986,7 +1008,10 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E, Result.Val.getComplexIntImag()); // FIXME: the target may want to specify that this is packed. - return llvm::ConstantStruct::get(VMContext, Complex, 2, false); + llvm::StructType *STy = llvm::StructType::get(Complex[0]->getType(), + Complex[1]->getType(), + NULL); + return llvm::ConstantStruct::get(STy, Complex); } case APValue::Float: return llvm::ConstantFP::get(VMContext, Result.Val.getFloat()); @@ -999,7 +1024,10 @@ llvm::Constant *CodeGenModule::EmitConstantExpr(const Expr *E, Result.Val.getComplexFloatImag()); // FIXME: the target may want to specify that this is packed. - return llvm::ConstantStruct::get(VMContext, Complex, 2, false); + llvm::StructType *STy = llvm::StructType::get(Complex[0]->getType(), + Complex[1]->getType(), + NULL); + return llvm::ConstantStruct::get(STy, Complex); } case APValue::Vector: { llvm::SmallVector<llvm::Constant *, 4> Inits; diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp index dff7bf45e0c3..a73e667e780e 100644 --- a/lib/CodeGen/CGExprScalar.cpp +++ b/lib/CodeGen/CGExprScalar.cpp @@ -82,15 +82,15 @@ public: LValue EmitLValue(const Expr *E) { return CGF.EmitLValue(E); } LValue EmitCheckedLValue(const Expr *E) { return CGF.EmitCheckedLValue(E); } - Value *EmitLoadOfLValue(LValue LV, QualType T) { - return CGF.EmitLoadOfLValue(LV, T).getScalarVal(); + Value *EmitLoadOfLValue(LValue LV) { + return CGF.EmitLoadOfLValue(LV).getScalarVal(); } /// EmitLoadOfLValue - Given an expression with complex type that represents a /// value l-value, this method emits the address of the l-value, then loads /// and returns the result. Value *EmitLoadOfLValue(const Expr *E) { - return EmitLoadOfLValue(EmitCheckedLValue(E), E->getType()); + return EmitLoadOfLValue(EmitCheckedLValue(E)); } /// EmitConversionToBool - Convert the specified expression value to a @@ -161,6 +161,9 @@ public: Value *VisitParenExpr(ParenExpr *PE) { return Visit(PE->getSubExpr()); } + Value *VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *E) { + return Visit(E->getReplacement()); + } Value *VisitGenericSelectionExpr(GenericSelectionExpr *GE) { return Visit(GE->getResultExpr()); } @@ -197,7 +200,7 @@ public: Value *VisitOpaqueValueExpr(OpaqueValueExpr *E) { if (E->isGLValue()) - return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E), E->getType()); + return EmitLoadOfLValue(CGF.getOpaqueLValueMapping(E)); // Otherwise, assume the mapping is the scalar directly. return CGF.getOpaqueRValueMapping(E).getScalarVal(); @@ -252,7 +255,7 @@ public: Value *VisitObjCIsaExpr(ObjCIsaExpr *E) { LValue LV = CGF.EmitObjCIsaExpr(E); - Value *V = CGF.EmitLoadOfLValue(LV, E->getType()).getScalarVal(); + Value *V = CGF.EmitLoadOfLValue(LV).getScalarVal(); return V; } @@ -269,14 +272,12 @@ public: Value *VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) { return CGF.CGM.EmitNullConstant(E->getType()); } - Value *VisitCastExpr(CastExpr *E) { - // Make sure to evaluate VLA bounds now so that we have them for later. + Value *VisitExplicitCastExpr(ExplicitCastExpr *E) { if (E->getType()->isVariablyModifiedType()) - CGF.EmitVLASize(E->getType()); - - return EmitCastExpr(E); + CGF.EmitVariablyModifiedType(E->getType()); + return VisitCastExpr(E); } - Value *EmitCastExpr(CastExpr *E); + Value *VisitCastExpr(CastExpr *E); Value *VisitCallExpr(const CallExpr *E) { if (E->getCallReturnType()->isReferenceType()) @@ -1001,7 +1002,7 @@ static bool ShouldNullCheckClassCastValue(const CastExpr *CE) { // VisitCastExpr - Emit code for an explicit or implicit cast. Implicit casts // have to handle a more broad range of conversions than explicit casts, as they // handle things like function to ptr-to-function decay etc. -Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { +Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { Expr *E = CE->getSubExpr(); QualType DestTy = CE->getType(); CastKind Kind = CE->getCastKind(); @@ -1020,7 +1021,7 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { Value *V = EmitLValue(E).getAddress(); V = Builder.CreateBitCast(V, ConvertType(CGF.getContext().getPointerType(DestTy))); - return EmitLoadOfLValue(CGF.MakeAddrLValue(V, DestTy), DestTy); + return EmitLoadOfLValue(CGF.MakeAddrLValue(V, DestTy)); } case CK_AnyPointerToObjCPointerCast: @@ -1106,7 +1107,17 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { // function pointers on Itanium and ARM). return CGF.CGM.getCXXABI().EmitMemberPointerConversion(CGF, CE, Src); } - + + case CK_ObjCProduceObject: + return CGF.EmitARCRetainScalarExpr(E); + case CK_ObjCConsumeObject: + return CGF.EmitObjCConsumeObject(E->getType(), Visit(E)); + case CK_ObjCReclaimReturnedObject: { + llvm::Value *value = Visit(E); + value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + return CGF.EmitObjCConsumeObject(E->getType(), value); + } + case CK_FloatingRealToComplex: case CK_FloatingComplexCast: case CK_IntegralRealToComplex: @@ -1122,7 +1133,7 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { assert(CGF.getContext().hasSameUnqualifiedType(E->getType(), DestTy)); assert(E->isGLValue() && E->getObjectKind() == OK_ObjCProperty && "CK_GetObjCProperty for non-lvalue or non-ObjCProperty"); - RValue RV = CGF.EmitLoadOfLValue(CGF.EmitLValue(E), E->getType()); + RValue RV = CGF.EmitLoadOfLValue(CGF.EmitLValue(E)); return RV.getScalarVal(); } @@ -1143,15 +1154,10 @@ Value *ScalarExprEmitter::EmitCastExpr(CastExpr *CE) { return Builder.CreateIntToPtr(IntResult, ConvertType(DestTy)); } - case CK_PointerToIntegral: { - Value *Src = Visit(const_cast<Expr*>(E)); - - // Handle conversion to bool correctly. - if (DestTy->isBooleanType()) - return EmitScalarConversion(Src, E->getType(), DestTy); + case CK_PointerToIntegral: + assert(!DestTy->isBooleanType() && "bool should use PointerToBool"); + return Builder.CreatePtrToInt(Visit(E), ConvertType(DestTy)); - return Builder.CreatePtrToInt(Src, ConvertType(DestTy)); - } case CK_ToVoid: { CGF.EmitIgnoredExpr(E); return 0; @@ -1221,7 +1227,7 @@ Value *ScalarExprEmitter::VisitStmtExpr(const StmtExpr *E) { Value *ScalarExprEmitter::VisitBlockDeclRefExpr(const BlockDeclRefExpr *E) { LValue LV = CGF.EmitBlockDeclRefLValue(E); - return CGF.EmitLoadOfLValue(LV, E->getType()).getScalarVal(); + return CGF.EmitLoadOfLValue(LV).getScalarVal(); } //===----------------------------------------------------------------------===// @@ -1258,7 +1264,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre) { QualType type = E->getSubExpr()->getType(); - llvm::Value *value = EmitLoadOfLValue(LV, type); + llvm::Value *value = EmitLoadOfLValue(LV); llvm::Value *input = value; int amount = (isInc ? 1 : -1); @@ -1282,7 +1288,7 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // overflow because of promotion rules; we're just eliding a few steps here. if (type->isSignedIntegerOrEnumerationType() && value->getType()->getPrimitiveSizeInBits() >= - CGF.CGM.IntTy->getBitWidth()) + CGF.IntTy->getBitWidth()) value = EmitAddConsiderOverflowBehavior(E, value, amt, isInc); else value = Builder.CreateAdd(value, amt, isInc ? "inc" : "dec"); @@ -1292,16 +1298,14 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, QualType type = ptr->getPointeeType(); // VLA types don't have constant size. - if (type->isVariableArrayType()) { - llvm::Value *vlaSize = - CGF.GetVLASize(CGF.getContext().getAsVariableArrayType(type)); - value = CGF.EmitCastToVoidPtr(value); - if (!isInc) vlaSize = Builder.CreateNSWNeg(vlaSize, "vla.negsize"); + if (const VariableArrayType *vla + = CGF.getContext().getAsVariableArrayType(type)) { + llvm::Value *numElts = CGF.getVLASize(vla).first; + if (!isInc) numElts = Builder.CreateNSWNeg(numElts, "vla.negsize"); if (CGF.getContext().getLangOptions().isSignedOverflowDefined()) - value = Builder.CreateGEP(value, vlaSize, "vla.inc"); + value = Builder.CreateGEP(value, numElts, "vla.inc"); else - value = Builder.CreateInBoundsGEP(value, vlaSize, "vla.inc"); - value = Builder.CreateBitCast(value, input->getType()); + value = Builder.CreateInBoundsGEP(value, numElts, "vla.inc"); // Arithmetic on function pointers (!) is just +-1. } else if (type->isFunctionType()) { @@ -1374,9 +1378,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV, // Store the updated result through the lvalue. if (LV.isBitField()) - CGF.EmitStoreThroughBitfieldLValue(RValue::get(value), LV, type, &value); + CGF.EmitStoreThroughBitfieldLValue(RValue::get(value), LV, &value); else - CGF.EmitStoreThroughLValue(RValue::get(value), LV, type); + CGF.EmitStoreThroughLValue(RValue::get(value), LV); // If this is a postinc, return the value read from memory, otherwise use the // updated value. @@ -1521,14 +1525,25 @@ ScalarExprEmitter::VisitUnaryExprOrTypeTraitExpr( CGF.getContext().getAsVariableArrayType(TypeToSize)) { if (E->isArgumentType()) { // sizeof(type) - make sure to emit the VLA size. - CGF.EmitVLASize(TypeToSize); + CGF.EmitVariablyModifiedType(TypeToSize); } else { // C99 6.5.3.4p2: If the argument is an expression of type // VLA, it is evaluated. CGF.EmitIgnoredExpr(E->getArgumentExpr()); } - return CGF.GetVLASize(VAT); + QualType eltType; + llvm::Value *numElts; + llvm::tie(numElts, eltType) = CGF.getVLASize(VAT); + + llvm::Value *size = numElts; + + // Scale the number of non-VLA elements by the non-VLA element size. + CharUnits eltSize = CGF.getContext().getTypeSizeInChars(eltType); + if (!eltSize.isOne()) + size = CGF.Builder.CreateNUWMul(CGF.CGM.getSize(eltSize), numElts); + + return size; } } @@ -1546,8 +1561,7 @@ Value *ScalarExprEmitter::VisitUnaryReal(const UnaryOperator *E) { // Note that we have to ask E because Op might be an l-value that // this won't work for, e.g. an Obj-C property. if (E->isGLValue()) - return CGF.EmitLoadOfLValue(CGF.EmitLValue(E), E->getType()) - .getScalarVal(); + return CGF.EmitLoadOfLValue(CGF.EmitLValue(E)).getScalarVal(); // Otherwise, calculate and project. return CGF.EmitComplexExpr(Op, false, true).first; @@ -1563,8 +1577,7 @@ Value *ScalarExprEmitter::VisitUnaryImag(const UnaryOperator *E) { // Note that we have to ask E because Op might be an l-value that // this won't work for, e.g. an Obj-C property. if (Op->isGLValue()) - return CGF.EmitLoadOfLValue(CGF.EmitLValue(E), E->getType()) - .getScalarVal(); + return CGF.EmitLoadOfLValue(CGF.EmitLValue(E)).getScalarVal(); // Otherwise, calculate and project. return CGF.EmitComplexExpr(Op, true, false).second; @@ -1616,7 +1629,7 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( OpInfo.E = E; // Load/convert the LHS. LValue LHSLV = EmitCheckedLValue(E->getLHS()); - OpInfo.LHS = EmitLoadOfLValue(LHSLV, LHSTy); + OpInfo.LHS = EmitLoadOfLValue(LHSLV); OpInfo.LHS = EmitScalarConversion(OpInfo.LHS, LHSTy, E->getComputationLHSType()); @@ -1631,10 +1644,9 @@ LValue ScalarExprEmitter::EmitCompoundAssignLValue( // 'An assignment expression has the value of the left operand after the // assignment...'. if (LHSLV.isBitField()) - CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, LHSTy, - &Result); + CGF.EmitStoreThroughBitfieldLValue(RValue::get(Result), LHSLV, &Result); else - CGF.EmitStoreThroughLValue(RValue::get(Result), LHSLV, LHSTy); + CGF.EmitStoreThroughLValue(RValue::get(Result), LHSLV); return LHSLV; } @@ -1662,15 +1674,17 @@ Value *ScalarExprEmitter::EmitCompoundAssign(const CompoundAssignOperator *E, return RHS; // Otherwise, reload the value. - return EmitLoadOfLValue(LHS, E->getType()); + return EmitLoadOfLValue(LHS); } void ScalarExprEmitter::EmitUndefinedBehaviorIntegerDivAndRemCheck( const BinOpInfo &Ops, llvm::Value *Zero, bool isDiv) { - llvm::BasicBlock *overflowBB = CGF.createBasicBlock("overflow", CGF.CurFn); + llvm::Function::iterator insertPt = Builder.GetInsertBlock(); llvm::BasicBlock *contBB = - CGF.createBasicBlock(isDiv ? "div.cont" : "rem.cont", CGF.CurFn); + CGF.createBasicBlock(isDiv ? "div.cont" : "rem.cont", CGF.CurFn, + llvm::next(insertPt)); + llvm::BasicBlock *overflowBB = CGF.createBasicBlock("overflow", CGF.CurFn); const llvm::IntegerType *Ty = cast<llvm::IntegerType>(Zero->getType()); @@ -1700,9 +1714,11 @@ Value *ScalarExprEmitter::EmitDiv(const BinOpInfo &Ops) { if (Ops.Ty->isIntegerType()) EmitUndefinedBehaviorIntegerDivAndRemCheck(Ops, Zero, true); else if (Ops.Ty->isRealFloatingType()) { + llvm::Function::iterator insertPt = Builder.GetInsertBlock(); + llvm::BasicBlock *DivCont = CGF.createBasicBlock("div.cont", CGF.CurFn, + llvm::next(insertPt)); llvm::BasicBlock *overflowBB = CGF.createBasicBlock("overflow", CGF.CurFn); - llvm::BasicBlock *DivCont = CGF.createBasicBlock("div.cont", CGF.CurFn); CGF.Builder.CreateCondBr(Builder.CreateFCmpOEQ(Ops.RHS, Zero), overflowBB, DivCont); EmitOverflowBB(overflowBB); @@ -1759,9 +1775,9 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) { OpID <<= 1; OpID |= 1; - const llvm::Type *opTy = CGF.CGM.getTypes().ConvertType(Ops.Ty); + llvm::Type *opTy = CGF.CGM.getTypes().ConvertType(Ops.Ty); - llvm::Function *intrinsic = CGF.CGM.getIntrinsic(IID, &opTy, 1); + llvm::Function *intrinsic = CGF.CGM.getIntrinsic(IID, opTy); Value *resultAndOverflow = Builder.CreateCall2(intrinsic, Ops.LHS, Ops.RHS); Value *result = Builder.CreateExtractValue(resultAndOverflow, 0); @@ -1769,8 +1785,10 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) { // Branch in case of overflow. llvm::BasicBlock *initialBB = Builder.GetInsertBlock(); + llvm::Function::iterator insertPt = initialBB; + llvm::BasicBlock *continueBB = CGF.createBasicBlock("nooverflow", CGF.CurFn, + llvm::next(insertPt)); llvm::BasicBlock *overflowBB = CGF.createBasicBlock("overflow", CGF.CurFn); - llvm::BasicBlock *continueBB = CGF.createBasicBlock("nooverflow", CGF.CurFn); Builder.CreateCondBr(overflow, overflowBB, continueBB); @@ -1788,8 +1806,8 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) { Builder.SetInsertPoint(overflowBB); // Get the overflow handler. - const llvm::Type *Int8Ty = llvm::Type::getInt8Ty(VMContext); - const llvm::Type *argTypes[] = { CGF.Int64Ty, CGF.Int64Ty, Int8Ty, Int8Ty }; + llvm::Type *Int8Ty = llvm::Type::getInt8Ty(VMContext); + llvm::Type *argTypes[] = { CGF.Int64Ty, CGF.Int64Ty, Int8Ty, Int8Ty }; llvm::FunctionType *handlerTy = llvm::FunctionType::get(CGF.Int64Ty, argTypes, true); llvm::Value *handler = CGF.CGM.CreateRuntimeFunction(handlerTy, *handlerName); @@ -1817,196 +1835,187 @@ Value *ScalarExprEmitter::EmitOverflowCheckedBinOp(const BinOpInfo &Ops) { return phi; } -Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &Ops) { - if (!Ops.Ty->isAnyPointerType()) { - if (Ops.Ty->isSignedIntegerOrEnumerationType()) { - switch (CGF.getContext().getLangOptions().getSignedOverflowBehavior()) { - case LangOptions::SOB_Undefined: - return Builder.CreateNSWAdd(Ops.LHS, Ops.RHS, "add"); - case LangOptions::SOB_Defined: - return Builder.CreateAdd(Ops.LHS, Ops.RHS, "add"); - case LangOptions::SOB_Trapping: - return EmitOverflowCheckedBinOp(Ops); - } - } - - if (Ops.LHS->getType()->isFPOrFPVectorTy()) - return Builder.CreateFAdd(Ops.LHS, Ops.RHS, "add"); - - return Builder.CreateAdd(Ops.LHS, Ops.RHS, "add"); - } - - // Must have binary (not unary) expr here. Unary pointer decrement doesn't - // use this path. - const BinaryOperator *BinOp = cast<BinaryOperator>(Ops.E); - - if (Ops.Ty->isPointerType() && - Ops.Ty->getAs<PointerType>()->isVariableArrayType()) { - // The amount of the addition needs to account for the VLA size - CGF.ErrorUnsupported(BinOp, "VLA pointer addition"); - } +/// Emit pointer + index arithmetic. +static Value *emitPointerArithmetic(CodeGenFunction &CGF, + const BinOpInfo &op, + bool isSubtraction) { + // Must have binary (not unary) expr here. Unary pointer + // increment/decrement doesn't use this path. + const BinaryOperator *expr = cast<BinaryOperator>(op.E); - Value *Ptr, *Idx; - Expr *IdxExp; - const PointerType *PT = BinOp->getLHS()->getType()->getAs<PointerType>(); - const ObjCObjectPointerType *OPT = - BinOp->getLHS()->getType()->getAs<ObjCObjectPointerType>(); - if (PT || OPT) { - Ptr = Ops.LHS; - Idx = Ops.RHS; - IdxExp = BinOp->getRHS(); - } else { // int + pointer - PT = BinOp->getRHS()->getType()->getAs<PointerType>(); - OPT = BinOp->getRHS()->getType()->getAs<ObjCObjectPointerType>(); - assert((PT || OPT) && "Invalid add expr"); - Ptr = Ops.RHS; - Idx = Ops.LHS; - IdxExp = BinOp->getLHS(); - } - - unsigned Width = cast<llvm::IntegerType>(Idx->getType())->getBitWidth(); - if (Width < CGF.PointerWidthInBits) { - // Zero or sign extend the pointer value based on whether the index is - // signed or not. - const llvm::Type *IdxType = CGF.IntPtrTy; - if (IdxExp->getType()->isSignedIntegerOrEnumerationType()) - Idx = Builder.CreateSExt(Idx, IdxType, "idx.ext"); - else - Idx = Builder.CreateZExt(Idx, IdxType, "idx.ext"); - } - const QualType ElementType = PT ? PT->getPointeeType() : OPT->getPointeeType(); - // Handle interface types, which are not represented with a concrete type. - if (const ObjCObjectType *OIT = ElementType->getAs<ObjCObjectType>()) { - llvm::Value *InterfaceSize = - llvm::ConstantInt::get(Idx->getType(), - CGF.getContext().getTypeSizeInChars(OIT).getQuantity()); - Idx = Builder.CreateMul(Idx, InterfaceSize); - const llvm::Type *i8Ty = llvm::Type::getInt8PtrTy(VMContext); - Value *Casted = Builder.CreateBitCast(Ptr, i8Ty); - Value *Res = Builder.CreateGEP(Casted, Idx, "add.ptr"); - return Builder.CreateBitCast(Res, Ptr->getType()); + Value *pointer = op.LHS; + Expr *pointerOperand = expr->getLHS(); + Value *index = op.RHS; + Expr *indexOperand = expr->getRHS(); + + // In a subtraction, the LHS is always the pointer. + if (!isSubtraction && !pointer->getType()->isPointerTy()) { + std::swap(pointer, index); + std::swap(pointerOperand, indexOperand); + } + + unsigned width = cast<llvm::IntegerType>(index->getType())->getBitWidth(); + if (width != CGF.PointerWidthInBits) { + // Zero-extend or sign-extend the pointer value according to + // whether the index is signed or not. + bool isSigned = indexOperand->getType()->isSignedIntegerOrEnumerationType(); + index = CGF.Builder.CreateIntCast(index, CGF.PtrDiffTy, isSigned, + "idx.ext"); + } + + // If this is subtraction, negate the index. + if (isSubtraction) + index = CGF.Builder.CreateNeg(index, "idx.neg"); + + const PointerType *pointerType + = pointerOperand->getType()->getAs<PointerType>(); + if (!pointerType) { + QualType objectType = pointerOperand->getType() + ->castAs<ObjCObjectPointerType>() + ->getPointeeType(); + llvm::Value *objectSize + = CGF.CGM.getSize(CGF.getContext().getTypeSizeInChars(objectType)); + + index = CGF.Builder.CreateMul(index, objectSize); + + Value *result = CGF.Builder.CreateBitCast(pointer, CGF.VoidPtrTy); + result = CGF.Builder.CreateGEP(result, index, "add.ptr"); + return CGF.Builder.CreateBitCast(result, pointer->getType()); + } + + QualType elementType = pointerType->getPointeeType(); + if (const VariableArrayType *vla + = CGF.getContext().getAsVariableArrayType(elementType)) { + // The element count here is the total number of non-VLA elements. + llvm::Value *numElements = CGF.getVLASize(vla).first; + + // Effectively, the multiply by the VLA size is part of the GEP. + // GEP indexes are signed, and scaling an index isn't permitted to + // signed-overflow, so we use the same semantics for our explicit + // multiply. We suppress this if overflow is not undefined behavior. + if (CGF.getLangOptions().isSignedOverflowDefined()) { + index = CGF.Builder.CreateMul(index, numElements, "vla.index"); + pointer = CGF.Builder.CreateGEP(pointer, index, "add.ptr"); + } else { + index = CGF.Builder.CreateNSWMul(index, numElements, "vla.index"); + pointer = CGF.Builder.CreateInBoundsGEP(pointer, index, "add.ptr"); + } + return pointer; } // Explicitly handle GNU void* and function pointer arithmetic extensions. The // GNU void* casts amount to no-ops since our void* type is i8*, but this is // future proof. - if (ElementType->isVoidType() || ElementType->isFunctionType()) { - const llvm::Type *i8Ty = llvm::Type::getInt8PtrTy(VMContext); - Value *Casted = Builder.CreateBitCast(Ptr, i8Ty); - Value *Res = Builder.CreateGEP(Casted, Idx, "add.ptr"); - return Builder.CreateBitCast(Res, Ptr->getType()); + if (elementType->isVoidType() || elementType->isFunctionType()) { + Value *result = CGF.Builder.CreateBitCast(pointer, CGF.VoidPtrTy); + result = CGF.Builder.CreateGEP(result, index, "add.ptr"); + return CGF.Builder.CreateBitCast(result, pointer->getType()); } - if (CGF.getContext().getLangOptions().isSignedOverflowDefined()) - return Builder.CreateGEP(Ptr, Idx, "add.ptr"); - return Builder.CreateInBoundsGEP(Ptr, Idx, "add.ptr"); + if (CGF.getLangOptions().isSignedOverflowDefined()) + return CGF.Builder.CreateGEP(pointer, index, "add.ptr"); + + return CGF.Builder.CreateInBoundsGEP(pointer, index, "add.ptr"); } -Value *ScalarExprEmitter::EmitSub(const BinOpInfo &Ops) { - if (!isa<llvm::PointerType>(Ops.LHS->getType())) { - if (Ops.Ty->isSignedIntegerOrEnumerationType()) { +Value *ScalarExprEmitter::EmitAdd(const BinOpInfo &op) { + if (op.LHS->getType()->isPointerTy() || + op.RHS->getType()->isPointerTy()) + return emitPointerArithmetic(CGF, op, /*subtraction*/ false); + + if (op.Ty->isSignedIntegerOrEnumerationType()) { + switch (CGF.getContext().getLangOptions().getSignedOverflowBehavior()) { + case LangOptions::SOB_Undefined: + return Builder.CreateNSWAdd(op.LHS, op.RHS, "add"); + case LangOptions::SOB_Defined: + return Builder.CreateAdd(op.LHS, op.RHS, "add"); + case LangOptions::SOB_Trapping: + return EmitOverflowCheckedBinOp(op); + } + } + + if (op.LHS->getType()->isFPOrFPVectorTy()) + return Builder.CreateFAdd(op.LHS, op.RHS, "add"); + + return Builder.CreateAdd(op.LHS, op.RHS, "add"); +} + +Value *ScalarExprEmitter::EmitSub(const BinOpInfo &op) { + // The LHS is always a pointer if either side is. + if (!op.LHS->getType()->isPointerTy()) { + if (op.Ty->isSignedIntegerOrEnumerationType()) { switch (CGF.getContext().getLangOptions().getSignedOverflowBehavior()) { case LangOptions::SOB_Undefined: - return Builder.CreateNSWSub(Ops.LHS, Ops.RHS, "sub"); + return Builder.CreateNSWSub(op.LHS, op.RHS, "sub"); case LangOptions::SOB_Defined: - return Builder.CreateSub(Ops.LHS, Ops.RHS, "sub"); + return Builder.CreateSub(op.LHS, op.RHS, "sub"); case LangOptions::SOB_Trapping: - return EmitOverflowCheckedBinOp(Ops); + return EmitOverflowCheckedBinOp(op); } } - if (Ops.LHS->getType()->isFPOrFPVectorTy()) - return Builder.CreateFSub(Ops.LHS, Ops.RHS, "sub"); + if (op.LHS->getType()->isFPOrFPVectorTy()) + return Builder.CreateFSub(op.LHS, op.RHS, "sub"); - return Builder.CreateSub(Ops.LHS, Ops.RHS, "sub"); + return Builder.CreateSub(op.LHS, op.RHS, "sub"); } - // Must have binary (not unary) expr here. Unary pointer increment doesn't - // use this path. - const BinaryOperator *BinOp = cast<BinaryOperator>(Ops.E); - - if (BinOp->getLHS()->getType()->isPointerType() && - BinOp->getLHS()->getType()->getAs<PointerType>()->isVariableArrayType()) { - // The amount of the addition needs to account for the VLA size for - // ptr-int - // The amount of the division needs to account for the VLA size for - // ptr-ptr. - CGF.ErrorUnsupported(BinOp, "VLA pointer subtraction"); - } - - const QualType LHSType = BinOp->getLHS()->getType(); - const QualType LHSElementType = LHSType->getPointeeType(); - if (!isa<llvm::PointerType>(Ops.RHS->getType())) { - // pointer - int - Value *Idx = Ops.RHS; - unsigned Width = cast<llvm::IntegerType>(Idx->getType())->getBitWidth(); - if (Width < CGF.PointerWidthInBits) { - // Zero or sign extend the pointer value based on whether the index is - // signed or not. - const llvm::Type *IdxType = CGF.IntPtrTy; - if (BinOp->getRHS()->getType()->isSignedIntegerOrEnumerationType()) - Idx = Builder.CreateSExt(Idx, IdxType, "idx.ext"); - else - Idx = Builder.CreateZExt(Idx, IdxType, "idx.ext"); - } - Idx = Builder.CreateNeg(Idx, "sub.ptr.neg"); - - // Handle interface types, which are not represented with a concrete type. - if (const ObjCObjectType *OIT = LHSElementType->getAs<ObjCObjectType>()) { - llvm::Value *InterfaceSize = - llvm::ConstantInt::get(Idx->getType(), - CGF.getContext(). - getTypeSizeInChars(OIT).getQuantity()); - Idx = Builder.CreateMul(Idx, InterfaceSize); - const llvm::Type *i8Ty = llvm::Type::getInt8PtrTy(VMContext); - Value *LHSCasted = Builder.CreateBitCast(Ops.LHS, i8Ty); - Value *Res = Builder.CreateGEP(LHSCasted, Idx, "add.ptr"); - return Builder.CreateBitCast(Res, Ops.LHS->getType()); - } + // If the RHS is not a pointer, then we have normal pointer + // arithmetic. + if (!op.RHS->getType()->isPointerTy()) + return emitPointerArithmetic(CGF, op, /*subtraction*/ true); - // Explicitly handle GNU void* and function pointer arithmetic - // extensions. The GNU void* casts amount to no-ops since our void* type is - // i8*, but this is future proof. - if (LHSElementType->isVoidType() || LHSElementType->isFunctionType()) { - const llvm::Type *i8Ty = llvm::Type::getInt8PtrTy(VMContext); - Value *LHSCasted = Builder.CreateBitCast(Ops.LHS, i8Ty); - Value *Res = Builder.CreateGEP(LHSCasted, Idx, "sub.ptr"); - return Builder.CreateBitCast(Res, Ops.LHS->getType()); - } + // Otherwise, this is a pointer subtraction. - if (CGF.getContext().getLangOptions().isSignedOverflowDefined()) - return Builder.CreateGEP(Ops.LHS, Idx, "sub.ptr"); - return Builder.CreateInBoundsGEP(Ops.LHS, Idx, "sub.ptr"); - } - - // pointer - pointer - Value *LHS = Ops.LHS; - Value *RHS = Ops.RHS; + // Do the raw subtraction part. + llvm::Value *LHS + = Builder.CreatePtrToInt(op.LHS, CGF.PtrDiffTy, "sub.ptr.lhs.cast"); + llvm::Value *RHS + = Builder.CreatePtrToInt(op.RHS, CGF.PtrDiffTy, "sub.ptr.rhs.cast"); + Value *diffInChars = Builder.CreateSub(LHS, RHS, "sub.ptr.sub"); - CharUnits ElementSize; + // Okay, figure out the element size. + const BinaryOperator *expr = cast<BinaryOperator>(op.E); + QualType elementType = expr->getLHS()->getType()->getPointeeType(); - // Handle GCC extension for pointer arithmetic on void* and function pointer - // types. - if (LHSElementType->isVoidType() || LHSElementType->isFunctionType()) - ElementSize = CharUnits::One(); - else - ElementSize = CGF.getContext().getTypeSizeInChars(LHSElementType); + llvm::Value *divisor = 0; - const llvm::Type *ResultType = ConvertType(Ops.Ty); - LHS = Builder.CreatePtrToInt(LHS, ResultType, "sub.ptr.lhs.cast"); - RHS = Builder.CreatePtrToInt(RHS, ResultType, "sub.ptr.rhs.cast"); - Value *BytesBetween = Builder.CreateSub(LHS, RHS, "sub.ptr.sub"); + // For a variable-length array, this is going to be non-constant. + if (const VariableArrayType *vla + = CGF.getContext().getAsVariableArrayType(elementType)) { + llvm::Value *numElements; + llvm::tie(numElements, elementType) = CGF.getVLASize(vla); - // Optimize out the shift for element size of 1. - if (ElementSize.isOne()) - return BytesBetween; + divisor = numElements; + // Scale the number of non-VLA elements by the non-VLA element size. + CharUnits eltSize = CGF.getContext().getTypeSizeInChars(elementType); + if (!eltSize.isOne()) + divisor = CGF.Builder.CreateNUWMul(CGF.CGM.getSize(eltSize), divisor); + + // For everything elese, we can just compute it, safe in the + // assumption that Sema won't let anything through that we can't + // safely compute the size of. + } else { + CharUnits elementSize; + // Handle GCC extension for pointer arithmetic on void* and + // function pointer types. + if (elementType->isVoidType() || elementType->isFunctionType()) + elementSize = CharUnits::One(); + else + elementSize = CGF.getContext().getTypeSizeInChars(elementType); + + // Don't even emit the divide for element size of 1. + if (elementSize.isOne()) + return diffInChars; + + divisor = CGF.CGM.getSize(elementSize); + } + // Otherwise, do a full sdiv. This uses the "exact" form of sdiv, since // pointer difference in C is only defined in the case where both operands // are pointing to elements of an array. - Value *BytesPerElt = - llvm::ConstantInt::get(ResultType, ElementSize.getQuantity()); - return Builder.CreateExactSDiv(BytesBetween, BytesPerElt, "sub.ptr.div"); + return Builder.CreateExactSDiv(diffInChars, divisor, "sub.ptr.div"); } Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) { @@ -2228,20 +2237,41 @@ Value *ScalarExprEmitter::EmitCompare(const BinaryOperator *E,unsigned UICmpOpc, Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { bool Ignore = TestAndClearIgnoreResultAssign(); - // __block variables need to have the rhs evaluated first, plus this should - // improve codegen just a little. - Value *RHS = Visit(E->getRHS()); - LValue LHS = EmitCheckedLValue(E->getLHS()); - - // Store the value into the LHS. Bit-fields are handled specially - // because the result is altered by the store, i.e., [C99 6.5.16p1] - // 'An assignment expression has the value of the left operand after - // the assignment...'. - if (LHS.isBitField()) - CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, E->getType(), - &RHS); - else - CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS, E->getType()); + Value *RHS; + LValue LHS; + + switch (E->getLHS()->getType().getObjCLifetime()) { + case Qualifiers::OCL_Strong: + llvm::tie(LHS, RHS) = CGF.EmitARCStoreStrong(E, Ignore); + break; + + case Qualifiers::OCL_Autoreleasing: + llvm::tie(LHS,RHS) = CGF.EmitARCStoreAutoreleasing(E); + break; + + case Qualifiers::OCL_Weak: + RHS = Visit(E->getRHS()); + LHS = EmitCheckedLValue(E->getLHS()); + RHS = CGF.EmitARCStoreWeak(LHS.getAddress(), RHS, Ignore); + break; + + // No reason to do any of these differently. + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + // __block variables need to have the rhs evaluated first, plus + // this should improve codegen just a little. + RHS = Visit(E->getRHS()); + LHS = EmitCheckedLValue(E->getLHS()); + + // Store the value into the LHS. Bit-fields are handled specially + // because the result is altered by the store, i.e., [C99 6.5.16p1] + // 'An assignment expression has the value of the left operand after + // the assignment...'. + if (LHS.isBitField()) + CGF.EmitStoreThroughBitfieldLValue(RValue::get(RHS), LHS, &RHS); + else + CGF.EmitStoreThroughLValue(RValue::get(RHS), LHS); + } // If the result is clearly ignored, return now. if (Ignore) @@ -2260,7 +2290,7 @@ Value *ScalarExprEmitter::VisitBinAssign(const BinaryOperator *E) { return RHS; // Otherwise, reload the value. - return EmitLoadOfLValue(LHS, E->getType()); + return EmitLoadOfLValue(LHS); } Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) { @@ -2548,7 +2578,7 @@ Value *ScalarExprEmitter::VisitBlockExpr(const BlockExpr *block) { Value *ScalarExprEmitter::VisitAsTypeExpr(AsTypeExpr *E) { Value *Src = CGF.EmitScalarExpr(E->getSrcExpr()); - const llvm::Type * DstTy = ConvertType(E->getDstType()); + const llvm::Type *DstTy = ConvertType(E->getType()); // Going from vec4->vec3 or vec3->vec4 is a special case and requires // a shuffle vector instead of a bitcast. @@ -2656,7 +2686,7 @@ LValue CodeGenFunction::EmitObjCIsaExpr(const ObjCIsaExpr *E) { llvm::Value *Src = EmitScalarExpr(BaseExpr); Builder.CreateStore(Src, V); V = ScalarExprEmitter(*this).EmitLoadOfLValue( - MakeAddrLValue(V, E->getType()), E->getType()); + MakeAddrLValue(V, E->getType())); } else { if (E->isArrow()) V = ScalarExprEmitter(*this).EmitLoadOfLValue(BaseExpr); diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp index fa42cd1f36ab..426cca00140c 100644 --- a/lib/CodeGen/CGObjC.cpp +++ b/lib/CodeGen/CGObjC.cpp @@ -15,15 +15,29 @@ #include "CGObjCRuntime.h" #include "CodeGenFunction.h" #include "CodeGenModule.h" +#include "TargetInfo.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtObjC.h" #include "clang/Basic/Diagnostic.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Target/TargetData.h" +#include "llvm/InlineAsm.h" using namespace clang; using namespace CodeGen; +typedef llvm::PointerIntPair<llvm::Value*,1,bool> TryEmitResult; +static TryEmitResult +tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e); + +/// Given the address of a variable of pointer type, find the correct +/// null to store into it. +static llvm::Constant *getNullForVariable(llvm::Value *addr) { + const llvm::Type *type = + cast<llvm::PointerType>(addr->getType())->getElementType(); + return llvm::ConstantPointerNull::get(cast<llvm::PointerType>(type)); +} + /// Emits an instance of NSConstantString representing the object. llvm::Value *CodeGenFunction::EmitObjCStringLiteral(const ObjCStringLiteral *E) { @@ -55,6 +69,7 @@ static RValue AdjustRelatedResultType(CodeGenFunction &CGF, RValue Result) { if (!Method) return Result; + if (!Method->hasRelatedResultType() || CGF.getContext().hasSameType(E->getType(), Method->getResultType()) || !Result.isScalar()) @@ -71,6 +86,18 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, // implementation vary between runtimes. We can get the receiver and // arguments in generic code. + bool isDelegateInit = E->isDelegateInitCall(); + + // We don't retain the receiver in delegate init calls, and this is + // safe because the receiver value is always loaded from 'self', + // which we zero out. We don't want to Block_copy block receivers, + // though. + bool retainSelf = + (!isDelegateInit && + CGM.getLangOptions().ObjCAutoRefCount && + E->getMethodDecl() && + E->getMethodDecl()->hasAttr<NSConsumesSelfAttr>()); + CGObjCRuntime &Runtime = CGM.getObjCRuntime(); bool isSuperMessage = false; bool isClassMessage = false; @@ -80,8 +107,15 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, llvm::Value *Receiver = 0; switch (E->getReceiverKind()) { case ObjCMessageExpr::Instance: - Receiver = EmitScalarExpr(E->getInstanceReceiver()); ReceiverType = E->getInstanceReceiver()->getType(); + if (retainSelf) { + TryEmitResult ter = tryEmitARCRetainScalarExpr(*this, + E->getInstanceReceiver()); + Receiver = ter.getPointer(); + if (!ter.getInt()) + Receiver = EmitARCRetainNonBlock(Receiver); + } else + Receiver = EmitScalarExpr(E->getInstanceReceiver()); break; case ObjCMessageExpr::Class: { @@ -92,6 +126,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, assert(OID && "Invalid Objective-C class message send"); Receiver = Runtime.GetClass(Builder, OID); isClassMessage = true; + + if (retainSelf) + Receiver = EmitARCRetainNonBlock(Receiver); break; } @@ -99,6 +136,9 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, ReceiverType = E->getSuperType(); Receiver = LoadObjCSelf(); isSuperMessage = true; + + if (retainSelf) + Receiver = EmitARCRetainNonBlock(Receiver); break; case ObjCMessageExpr::SuperClass: @@ -106,14 +146,36 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, Receiver = LoadObjCSelf(); isSuperMessage = true; isClassMessage = true; + + if (retainSelf) + Receiver = EmitARCRetainNonBlock(Receiver); break; } + QualType ResultType = + E->getMethodDecl() ? E->getMethodDecl()->getResultType() : E->getType(); + CallArgList Args; EmitCallArgs(Args, E->getMethodDecl(), E->arg_begin(), E->arg_end()); - QualType ResultType = - E->getMethodDecl() ? E->getMethodDecl()->getResultType() : E->getType(); + // For delegate init calls in ARC, do an unsafe store of null into + // self. This represents the call taking direct ownership of that + // value. We have to do this after emitting the other call + // arguments because they might also reference self, but we don't + // have to worry about any of them modifying self because that would + // be an undefined read and write of an object in unordered + // expressions. + if (isDelegateInit) { + assert(getLangOptions().ObjCAutoRefCount && + "delegate init calls should only be marked in ARC"); + + // Do an unsafe store of null into self. + llvm::Value *selfAddr = + LocalDeclMap[cast<ObjCMethodDecl>(CurCodeDecl)->getSelfDecl()]; + assert(selfAddr && "no self entry for a delegate init call?"); + + Builder.CreateStore(getNullForVariable(selfAddr), selfAddr); + } RValue result; if (isSuperMessage) { @@ -134,10 +196,54 @@ RValue CodeGenFunction::EmitObjCMessageExpr(const ObjCMessageExpr *E, Receiver, Args, OID, E->getMethodDecl()); } - + + // For delegate init calls in ARC, implicitly store the result of + // the call back into self. This takes ownership of the value. + if (isDelegateInit) { + llvm::Value *selfAddr = + LocalDeclMap[cast<ObjCMethodDecl>(CurCodeDecl)->getSelfDecl()]; + llvm::Value *newSelf = result.getScalarVal(); + + // The delegate return type isn't necessarily a matching type; in + // fact, it's quite likely to be 'id'. + const llvm::Type *selfTy = + cast<llvm::PointerType>(selfAddr->getType())->getElementType(); + newSelf = Builder.CreateBitCast(newSelf, selfTy); + + Builder.CreateStore(newSelf, selfAddr); + } + return AdjustRelatedResultType(*this, E, E->getMethodDecl(), result); } +namespace { +struct FinishARCDealloc : EHScopeStack::Cleanup { + void Emit(CodeGenFunction &CGF, Flags flags) { + const ObjCMethodDecl *method = cast<ObjCMethodDecl>(CGF.CurCodeDecl); + + const ObjCImplDecl *impl = cast<ObjCImplDecl>(method->getDeclContext()); + const ObjCInterfaceDecl *iface = impl->getClassInterface(); + if (!iface->getSuperClass()) return; + + bool isCategory = isa<ObjCCategoryImplDecl>(impl); + + // Call [super dealloc] if we have a superclass. + llvm::Value *self = CGF.LoadObjCSelf(); + + CallArgList args; + CGF.CGM.getObjCRuntime().GenerateMessageSendSuper(CGF, ReturnValueSlot(), + CGF.getContext().VoidTy, + method->getSelector(), + iface, + isCategory, + self, + /*is class msg*/ false, + args, + method); + } +}; +} + /// StartObjCMethod - Begin emission of an ObjCMethod. This generates /// the LLVM function and sets the other context used by /// CodeGenFunction. @@ -164,8 +270,21 @@ void CodeGenFunction::StartObjCMethod(const ObjCMethodDecl *OMD, CurGD = OMD; StartFunction(OMD, OMD->getResultType(), Fn, FI, args, StartLoc); + + // In ARC, certain methods get an extra cleanup. + if (CGM.getLangOptions().ObjCAutoRefCount && + OMD->isInstanceMethod() && + OMD->getSelector().isUnarySelector()) { + const IdentifierInfo *ident = + OMD->getSelector().getIdentifierInfoForSlot(0); + if (ident->isStr("dealloc")) + EHStack.pushCleanup<FinishARCDealloc>(getARCCleanupKind()); + } } +static llvm::Value *emitARCRetainLoadOfScalar(CodeGenFunction &CGF, + LValue lvalue, QualType type); + void CodeGenFunction::GenerateObjCGetterBody(ObjCIvarDecl *Ivar, bool IsAtomic, bool IsStrong) { LValue LV = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), @@ -269,6 +388,9 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, RV = RValue::get(Builder.CreateBitCast(RV.getScalarVal(), Types.ConvertType(PD->getType()))); EmitReturnOfRValue(RV, PD->getType()); + + // objc_getProperty does an autorelease, so we should suppress ours. + AutoreleaseResult = false; } else { const llvm::Triple &Triple = getContext().Target.getTriple(); QualType IVART = Ivar->getType(); @@ -346,18 +468,24 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP, } else { LValue LV = EmitLValueForIvar(TypeOfSelfObject(), LoadObjCSelf(), - Ivar, 0); - if (PD->getType()->isReferenceType()) { - RValue RV = RValue::get(LV.getAddress()); - EmitReturnOfRValue(RV, PD->getType()); - } - else { - CodeGenTypes &Types = CGM.getTypes(); - RValue RV = EmitLoadOfLValue(LV, IVART); - RV = RValue::get(Builder.CreateBitCast(RV.getScalarVal(), - Types.ConvertType(PD->getType()))); - EmitReturnOfRValue(RV, PD->getType()); + Ivar, 0); + QualType propType = PD->getType(); + + llvm::Value *value; + if (propType->isReferenceType()) { + value = LV.getAddress(); + } else { + // In ARC, we want to emit this retained. + if (getLangOptions().ObjCAutoRefCount && + PD->getType()->isObjCRetainableType()) + value = emitARCRetainLoadOfScalar(*this, LV, IVART); + else + value = EmitLoadOfLValue(LV).getScalarVal(); + + value = Builder.CreateBitCast(value, ConvertType(propType)); } + + EmitReturnOfRValue(RValue::get(value), propType); } } @@ -551,50 +679,35 @@ void CodeGenFunction::GenerateObjCSetter(ObjCImplementationDecl *IMP, FinishFunction(); } -// FIXME: these are stolen from CGClass.cpp, which is lame. namespace { - struct CallArrayIvarDtor : EHScopeStack::Cleanup { + struct DestroyIvar : EHScopeStack::Cleanup { + private: + llvm::Value *addr; const ObjCIvarDecl *ivar; - llvm::Value *self; - CallArrayIvarDtor(const ObjCIvarDecl *ivar, llvm::Value *self) - : ivar(ivar), self(self) {} - - void Emit(CodeGenFunction &CGF, bool IsForEH) { - LValue lvalue = - CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), self, ivar, 0); - - QualType type = ivar->getType(); - const ConstantArrayType *arrayType - = CGF.getContext().getAsConstantArrayType(type); - QualType baseType = CGF.getContext().getBaseElementType(arrayType); - const CXXRecordDecl *classDecl = baseType->getAsCXXRecordDecl(); - - llvm::Value *base - = CGF.Builder.CreateBitCast(lvalue.getAddress(), - CGF.ConvertType(baseType)->getPointerTo()); - CGF.EmitCXXAggrDestructorCall(classDecl->getDestructor(), - arrayType, base); + CodeGenFunction::Destroyer &destroyer; + bool useEHCleanupForArray; + public: + DestroyIvar(llvm::Value *addr, const ObjCIvarDecl *ivar, + CodeGenFunction::Destroyer *destroyer, + bool useEHCleanupForArray) + : addr(addr), ivar(ivar), destroyer(*destroyer), + useEHCleanupForArray(useEHCleanupForArray) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + LValue lvalue + = CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), addr, ivar, /*CVR*/ 0); + CGF.emitDestroy(lvalue.getAddress(), ivar->getType(), destroyer, + flags.isForNormalCleanup() && useEHCleanupForArray); } }; +} - struct CallIvarDtor : EHScopeStack::Cleanup { - const ObjCIvarDecl *ivar; - llvm::Value *self; - CallIvarDtor(const ObjCIvarDecl *ivar, llvm::Value *self) - : ivar(ivar), self(self) {} - - void Emit(CodeGenFunction &CGF, bool IsForEH) { - LValue lvalue = - CGF.EmitLValueForIvar(CGF.TypeOfSelfObject(), self, ivar, 0); - - QualType type = ivar->getType(); - const CXXRecordDecl *classDecl = type->getAsCXXRecordDecl(); - - CGF.EmitCXXDestructorCall(classDecl->getDestructor(), - Dtor_Complete, /*ForVirtualBase=*/false, - lvalue.getAddress()); - } - }; +/// Like CodeGenFunction::destroyARCStrong, but do it with a call. +static void destroyARCStrongWithStore(CodeGenFunction &CGF, + llvm::Value *addr, + QualType type) { + llvm::Value *null = getNullForVariable(addr); + CGF.EmitARCStoreStrongCall(addr, null, /*ignored*/ true); } static void emitCXXDestructMethod(CodeGenFunction &CGF, @@ -609,29 +722,26 @@ static void emitCXXDestructMethod(CodeGenFunction &CGF, ivar; ivar = ivar->getNextIvar()) { QualType type = ivar->getType(); - // Drill down to the base element type. - QualType baseType = type; - const ConstantArrayType *arrayType = - CGF.getContext().getAsConstantArrayType(baseType); - if (arrayType) baseType = CGF.getContext().getBaseElementType(arrayType); - // Check whether the ivar is a destructible type. - QualType::DestructionKind destructKind = baseType.isDestructedType(); - assert(destructKind == type.isDestructedType()); - - switch (destructKind) { - case QualType::DK_none: - continue; - - case QualType::DK_cxx_destructor: - if (arrayType) - CGF.EHStack.pushCleanup<CallArrayIvarDtor>(NormalAndEHCleanup, - ivar, self); - else - CGF.EHStack.pushCleanup<CallIvarDtor>(NormalAndEHCleanup, - ivar, self); - break; + QualType::DestructionKind dtorKind = type.isDestructedType(); + if (!dtorKind) continue; + + CodeGenFunction::Destroyer *destroyer = 0; + + // Use a call to objc_storeStrong to destroy strong ivars, for the + // general benefit of the tools. + if (dtorKind == QualType::DK_objc_strong_lifetime) { + destroyer = &destroyARCStrongWithStore; + + // Otherwise use the default for the destruction kind. + } else { + destroyer = &CGF.getDestroyer(dtorKind); } + + CleanupKind cleanupKind = CGF.getCleanupKind(dtorKind); + + CGF.EHStack.pushCleanup<DestroyIvar>(cleanupKind, self, ivar, destroyer, + cleanupKind & EHCleanup); } assert(scope.requiresCleanups() && "nothing to do in .cxx_destruct?"); @@ -645,6 +755,9 @@ void CodeGenFunction::GenerateObjCCtorDtorMethod(ObjCImplementationDecl *IMP, // Emit .cxx_construct. if (ctor) { + // Suppress the final autorelease in ARC. + AutoreleaseResult = false; + llvm::SmallVector<CXXCtorInitializer *, 8> IvarInitializers; for (ObjCImplementationDecl::init_const_iterator B = IMP->init_begin(), E = IMP->init_end(); B != E; ++B) { @@ -747,6 +860,16 @@ RValue CodeGenFunction::EmitLoadOfPropertyRefLValue(LValue LV, llvm::Value *Receiver = LV.getPropertyRefBaseAddr(); + if (CGM.getLangOptions().ObjCAutoRefCount) { + QualType receiverType; + if (E->isSuperReceiver()) + receiverType = E->getSuperReceiverType(); + else if (E->isClassReceiver()) + receiverType = getContext().getObjCClassType(); + else + receiverType = E->getBase()->getType(); + } + // Accesses to 'super' follow a different code path. if (E->isSuperReceiver()) return AdjustRelatedResultType(*this, E, method, @@ -757,9 +880,9 @@ RValue CodeGenFunction::EmitLoadOfPropertyRefLValue(LValue LV, const ObjCInterfaceDecl *ReceiverClass = (E->isClassReceiver() ? E->getClassReceiver() : 0); return AdjustRelatedResultType(*this, E, method, - CGM.getObjCRuntime(). - GenerateMessageSend(*this, Return, ResultType, S, - Receiver, CallArgList(), ReceiverClass)); + CGM.getObjCRuntime(). + GenerateMessageSend(*this, Return, ResultType, S, + Receiver, CallArgList(), ReceiverClass)); } void CodeGenFunction::EmitStoreThroughPropertyRefLValue(RValue Src, @@ -808,17 +931,17 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ return; } - // The local variable comes into scope immediately. - AutoVarEmission variable = AutoVarEmission::invalid(); - if (const DeclStmt *SD = dyn_cast<DeclStmt>(S.getElement())) - variable = EmitAutoVarAlloca(*cast<VarDecl>(SD->getSingleDecl())); - CGDebugInfo *DI = getDebugInfo(); if (DI) { DI->setLocation(S.getSourceRange().getBegin()); DI->EmitRegionStart(Builder); } + // The local variable comes into scope immediately. + AutoVarEmission variable = AutoVarEmission::invalid(); + if (const DeclStmt *SD = dyn_cast<DeclStmt>(S.getElement())) + variable = EmitAutoVarAlloca(*cast<VarDecl>(SD->getSingleDecl())); + JumpDest LoopEnd = getJumpDestInCurrentScope("forcoll.end"); JumpDest AfterBody = getJumpDestInCurrentScope("forcoll.next"); @@ -958,6 +1081,9 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ elementLValue = EmitLValue(&tempDRE); elementType = D->getType(); elementIsVariable = true; + + if (D->isARCPseudoStrong()) + elementLValue.getQuals().setObjCLifetime(Qualifiers::OCL_ExplicitNone); } else { elementLValue = LValue(); // suppress warning elementType = cast<Expr>(S.getElement())->getType(); @@ -984,10 +1110,12 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ // Make sure we have an l-value. Yes, this gets evaluated every // time through the loop. - if (!elementIsVariable) + if (!elementIsVariable) { elementLValue = EmitLValue(cast<Expr>(S.getElement())); - - EmitStoreThroughLValue(RValue::get(CurrentItem), elementLValue, elementType); + EmitStoreThroughLValue(RValue::get(CurrentItem), elementLValue); + } else { + EmitScalarInit(CurrentItem, elementLValue); + } // If we do have an element variable, this assignment is the end of // its initialization. @@ -1048,7 +1176,7 @@ void CodeGenFunction::EmitObjCForCollectionStmt(const ObjCForCollectionStmt &S){ llvm::Value *null = llvm::Constant::getNullValue(convertedElementType); elementLValue = EmitLValue(cast<Expr>(S.getElement())); - EmitStoreThroughLValue(RValue::get(null), elementLValue, elementType); + EmitStoreThroughLValue(RValue::get(null), elementLValue); } if (DI) { @@ -1072,4 +1200,957 @@ void CodeGenFunction::EmitObjCAtSynchronizedStmt( CGM.getObjCRuntime().EmitSynchronizedStmt(*this, S); } +/// Produce the code for a CK_ObjCProduceObject. Just does a +/// primitive retain. +llvm::Value *CodeGenFunction::EmitObjCProduceObject(QualType type, + llvm::Value *value) { + return EmitARCRetain(type, value); +} + +namespace { + struct CallObjCRelease : EHScopeStack::Cleanup { + CallObjCRelease(QualType type, llvm::Value *ptr, llvm::Value *condition) + : type(type), ptr(ptr), condition(condition) {} + QualType type; + llvm::Value *ptr; + llvm::Value *condition; + + void Emit(CodeGenFunction &CGF, Flags flags) { + llvm::Value *object; + + // If we're in a conditional branch, we had to stash away in an + // alloca the pointer to be released. + llvm::BasicBlock *cont = 0; + if (condition) { + llvm::BasicBlock *release = CGF.createBasicBlock("release.yes"); + cont = CGF.createBasicBlock("release.cont"); + + llvm::Value *cond = CGF.Builder.CreateLoad(condition); + CGF.Builder.CreateCondBr(cond, release, cont); + CGF.EmitBlock(release); + object = CGF.Builder.CreateLoad(ptr); + } else { + object = ptr; + } + + CGF.EmitARCRelease(object, /*precise*/ true); + + if (cont) CGF.EmitBlock(cont); + } + }; +} + +/// Produce the code for a CK_ObjCConsumeObject. Does a primitive +/// release at the end of the full-expression. +llvm::Value *CodeGenFunction::EmitObjCConsumeObject(QualType type, + llvm::Value *object) { + // If we're in a conditional branch, we need to make the cleanup + // conditional. FIXME: this really needs to be supported by the + // environment. + llvm::AllocaInst *cond; + llvm::Value *ptr; + if (isInConditionalBranch()) { + cond = CreateTempAlloca(Builder.getInt1Ty(), "release.cond"); + ptr = CreateTempAlloca(object->getType(), "release.value"); + + // The alloca is false until we get here. + // FIXME: er. doesn't this need to be set at the start of the condition? + InitTempAlloca(cond, Builder.getFalse()); + + // Then it turns true. + Builder.CreateStore(Builder.getTrue(), cond); + Builder.CreateStore(object, ptr); + } else { + cond = 0; + ptr = object; + } + + EHStack.pushCleanup<CallObjCRelease>(getARCCleanupKind(), type, ptr, cond); + return object; +} + +llvm::Value *CodeGenFunction::EmitObjCExtendObjectLifetime(QualType type, + llvm::Value *value) { + return EmitARCRetainAutorelease(type, value); +} + + +static llvm::Constant *createARCRuntimeFunction(CodeGenModule &CGM, + const llvm::FunctionType *type, + llvm::StringRef fnName) { + llvm::Constant *fn = CGM.CreateRuntimeFunction(type, fnName); + + // In -fobjc-no-arc-runtime, emit weak references to the runtime + // support library. + if (!CGM.getCodeGenOpts().ObjCRuntimeHasARC) + if (llvm::Function *f = dyn_cast<llvm::Function>(fn)) + f->setLinkage(llvm::Function::ExternalWeakLinkage); + + return fn; +} + +/// Perform an operation having the signature +/// i8* (i8*) +/// where a null input causes a no-op and returns null. +static llvm::Value *emitARCValueOperation(CodeGenFunction &CGF, + llvm::Value *value, + llvm::Constant *&fn, + llvm::StringRef fnName) { + if (isa<llvm::ConstantPointerNull>(value)) return value; + + if (!fn) { + std::vector<llvm::Type*> args(1, CGF.Int8PtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(CGF.Int8PtrTy, args, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + // Cast the argument to 'id'. + const llvm::Type *origType = value->getType(); + value = CGF.Builder.CreateBitCast(value, CGF.Int8PtrTy); + + // Call the function. + llvm::CallInst *call = CGF.Builder.CreateCall(fn, value); + call->setDoesNotThrow(); + + // Cast the result back to the original type. + return CGF.Builder.CreateBitCast(call, origType); +} + +/// Perform an operation having the following signature: +/// i8* (i8**) +static llvm::Value *emitARCLoadOperation(CodeGenFunction &CGF, + llvm::Value *addr, + llvm::Constant *&fn, + llvm::StringRef fnName) { + if (!fn) { + std::vector<llvm::Type*> args(1, CGF.Int8PtrPtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(CGF.Int8PtrTy, args, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + // Cast the argument to 'id*'. + const llvm::Type *origType = addr->getType(); + addr = CGF.Builder.CreateBitCast(addr, CGF.Int8PtrPtrTy); + + // Call the function. + llvm::CallInst *call = CGF.Builder.CreateCall(fn, addr); + call->setDoesNotThrow(); + + // Cast the result back to a dereference of the original type. + llvm::Value *result = call; + if (origType != CGF.Int8PtrPtrTy) + result = CGF.Builder.CreateBitCast(result, + cast<llvm::PointerType>(origType)->getElementType()); + + return result; +} + +/// Perform an operation having the following signature: +/// i8* (i8**, i8*) +static llvm::Value *emitARCStoreOperation(CodeGenFunction &CGF, + llvm::Value *addr, + llvm::Value *value, + llvm::Constant *&fn, + llvm::StringRef fnName, + bool ignored) { + assert(cast<llvm::PointerType>(addr->getType())->getElementType() + == value->getType()); + + if (!fn) { + std::vector<llvm::Type*> argTypes(2); + argTypes[0] = CGF.Int8PtrPtrTy; + argTypes[1] = CGF.Int8PtrTy; + + const llvm::FunctionType *fnType + = llvm::FunctionType::get(CGF.Int8PtrTy, argTypes, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + const llvm::Type *origType = value->getType(); + + addr = CGF.Builder.CreateBitCast(addr, CGF.Int8PtrPtrTy); + value = CGF.Builder.CreateBitCast(value, CGF.Int8PtrTy); + + llvm::CallInst *result = CGF.Builder.CreateCall2(fn, addr, value); + result->setDoesNotThrow(); + + if (ignored) return 0; + + return CGF.Builder.CreateBitCast(result, origType); +} + +/// Perform an operation having the following signature: +/// void (i8**, i8**) +static void emitARCCopyOperation(CodeGenFunction &CGF, + llvm::Value *dst, + llvm::Value *src, + llvm::Constant *&fn, + llvm::StringRef fnName) { + assert(dst->getType() == src->getType()); + + if (!fn) { + std::vector<llvm::Type*> argTypes(2, CGF.Int8PtrPtrTy); + const llvm::FunctionType *fnType + = llvm::FunctionType::get(CGF.Builder.getVoidTy(), argTypes, false); + fn = createARCRuntimeFunction(CGF.CGM, fnType, fnName); + } + + dst = CGF.Builder.CreateBitCast(dst, CGF.Int8PtrPtrTy); + src = CGF.Builder.CreateBitCast(src, CGF.Int8PtrPtrTy); + + llvm::CallInst *result = CGF.Builder.CreateCall2(fn, dst, src); + result->setDoesNotThrow(); +} + +/// Produce the code to do a retain. Based on the type, calls one of: +/// call i8* @objc_retain(i8* %value) +/// call i8* @objc_retainBlock(i8* %value) +llvm::Value *CodeGenFunction::EmitARCRetain(QualType type, llvm::Value *value) { + if (type->isBlockPointerType()) + return EmitARCRetainBlock(value); + else + return EmitARCRetainNonBlock(value); +} + +/// Retain the given object, with normal retain semantics. +/// call i8* @objc_retain(i8* %value) +llvm::Value *CodeGenFunction::EmitARCRetainNonBlock(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retain, + "objc_retain"); +} + +/// Retain the given block, with _Block_copy semantics. +/// call i8* @objc_retainBlock(i8* %value) +llvm::Value *CodeGenFunction::EmitARCRetainBlock(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainBlock, + "objc_retainBlock"); +} + +/// Retain the given object which is the result of a function call. +/// call i8* @objc_retainAutoreleasedReturnValue(i8* %value) +/// +/// Yes, this function name is one character away from a different +/// call with completely different semantics. +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleasedReturnValue(llvm::Value *value) { + // Fetch the void(void) inline asm which marks that we're going to + // retain the autoreleased return value. + llvm::InlineAsm *&marker + = CGM.getARCEntrypoints().retainAutoreleasedReturnValueMarker; + if (!marker) { + llvm::StringRef assembly + = CGM.getTargetCodeGenInfo() + .getARCRetainAutoreleasedReturnValueMarker(); + + // If we have an empty assembly string, there's nothing to do. + if (assembly.empty()) { + + // Otherwise, at -O0, build an inline asm that we're going to call + // in a moment. + } else if (CGM.getCodeGenOpts().OptimizationLevel == 0) { + llvm::FunctionType *type = + llvm::FunctionType::get(llvm::Type::getVoidTy(getLLVMContext()), + /*variadic*/ false); + + marker = llvm::InlineAsm::get(type, assembly, "", /*sideeffects*/ true); + + // If we're at -O1 and above, we don't want to litter the code + // with this marker yet, so leave a breadcrumb for the ARC + // optimizer to pick up. + } else { + llvm::NamedMDNode *metadata = + CGM.getModule().getOrInsertNamedMetadata( + "clang.arc.retainAutoreleasedReturnValueMarker"); + assert(metadata->getNumOperands() <= 1); + if (metadata->getNumOperands() == 0) { + llvm::Value *string = llvm::MDString::get(getLLVMContext(), assembly); + llvm::Value *args[] = { string }; + metadata->addOperand(llvm::MDNode::get(getLLVMContext(), args)); + } + } + } + + // Call the marker asm if we made one, which we do only at -O0. + if (marker) Builder.CreateCall(marker); + + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainAutoreleasedReturnValue, + "objc_retainAutoreleasedReturnValue"); +} + +/// Release the given object. +/// call void @objc_release(i8* %value) +void CodeGenFunction::EmitARCRelease(llvm::Value *value, bool precise) { + if (isa<llvm::ConstantPointerNull>(value)) return; + + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_release; + if (!fn) { + std::vector<llvm::Type*> args(1, Int8PtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), args, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_release"); + } + + // Cast the argument to 'id'. + value = Builder.CreateBitCast(value, Int8PtrTy); + + // Call objc_release. + llvm::CallInst *call = Builder.CreateCall(fn, value); + call->setDoesNotThrow(); + + if (!precise) { + llvm::SmallVector<llvm::Value*,1> args; + call->setMetadata("clang.imprecise_release", + llvm::MDNode::get(Builder.getContext(), args)); + } +} + +/// Store into a strong object. Always calls this: +/// call void @objc_storeStrong(i8** %addr, i8* %value) +llvm::Value *CodeGenFunction::EmitARCStoreStrongCall(llvm::Value *addr, + llvm::Value *value, + bool ignored) { + assert(cast<llvm::PointerType>(addr->getType())->getElementType() + == value->getType()); + + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_storeStrong; + if (!fn) { + llvm::Type *argTypes[] = { Int8PtrPtrTy, Int8PtrTy }; + const llvm::FunctionType *fnType + = llvm::FunctionType::get(Builder.getVoidTy(), argTypes, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_storeStrong"); + } + + addr = Builder.CreateBitCast(addr, Int8PtrPtrTy); + llvm::Value *castValue = Builder.CreateBitCast(value, Int8PtrTy); + + Builder.CreateCall2(fn, addr, castValue)->setDoesNotThrow(); + + if (ignored) return 0; + return value; +} + +/// Store into a strong object. Sometimes calls this: +/// call void @objc_storeStrong(i8** %addr, i8* %value) +/// Other times, breaks it down into components. +llvm::Value *CodeGenFunction::EmitARCStoreStrong(LValue dst, + llvm::Value *newValue, + bool ignored) { + QualType type = dst.getType(); + bool isBlock = type->isBlockPointerType(); + + // Use a store barrier at -O0 unless this is a block type or the + // lvalue is inadequately aligned. + if (shouldUseFusedARCCalls() && + !isBlock && + !(dst.getAlignment() && dst.getAlignment() < PointerAlignInBytes)) { + return EmitARCStoreStrongCall(dst.getAddress(), newValue, ignored); + } + + // Otherwise, split it out. + + // Retain the new value. + newValue = EmitARCRetain(type, newValue); + + // Read the old value. + llvm::Value *oldValue = EmitLoadOfScalar(dst); + + // Store. We do this before the release so that any deallocs won't + // see the old value. + EmitStoreOfScalar(newValue, dst); + + // Finally, release the old value. + EmitARCRelease(oldValue, /*precise*/ false); + + return newValue; +} + +/// Autorelease the given object. +/// call i8* @objc_autorelease(i8* %value) +llvm::Value *CodeGenFunction::EmitARCAutorelease(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_autorelease, + "objc_autorelease"); +} + +/// Autorelease the given object. +/// call i8* @objc_autoreleaseReturnValue(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCAutoreleaseReturnValue(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_autoreleaseReturnValue, + "objc_autoreleaseReturnValue"); +} + +/// Do a fused retain/autorelease of the given object. +/// call i8* @objc_retainAutoreleaseReturnValue(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleaseReturnValue(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainAutoreleaseReturnValue, + "objc_retainAutoreleaseReturnValue"); +} + +/// Do a fused retain/autorelease of the given object. +/// call i8* @objc_retainAutorelease(i8* %value) +/// or +/// %retain = call i8* @objc_retainBlock(i8* %value) +/// call i8* @objc_autorelease(i8* %retain) +llvm::Value *CodeGenFunction::EmitARCRetainAutorelease(QualType type, + llvm::Value *value) { + if (!type->isBlockPointerType()) + return EmitARCRetainAutoreleaseNonBlock(value); + + if (isa<llvm::ConstantPointerNull>(value)) return value; + + const llvm::Type *origType = value->getType(); + value = Builder.CreateBitCast(value, Int8PtrTy); + value = EmitARCRetainBlock(value); + value = EmitARCAutorelease(value); + return Builder.CreateBitCast(value, origType); +} + +/// Do a fused retain/autorelease of the given object. +/// call i8* @objc_retainAutorelease(i8* %value) +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleaseNonBlock(llvm::Value *value) { + return emitARCValueOperation(*this, value, + CGM.getARCEntrypoints().objc_retainAutorelease, + "objc_retainAutorelease"); +} + +/// i8* @objc_loadWeak(i8** %addr) +/// Essentially objc_autorelease(objc_loadWeakRetained(addr)). +llvm::Value *CodeGenFunction::EmitARCLoadWeak(llvm::Value *addr) { + return emitARCLoadOperation(*this, addr, + CGM.getARCEntrypoints().objc_loadWeak, + "objc_loadWeak"); +} + +/// i8* @objc_loadWeakRetained(i8** %addr) +llvm::Value *CodeGenFunction::EmitARCLoadWeakRetained(llvm::Value *addr) { + return emitARCLoadOperation(*this, addr, + CGM.getARCEntrypoints().objc_loadWeakRetained, + "objc_loadWeakRetained"); +} + +/// i8* @objc_storeWeak(i8** %addr, i8* %value) +/// Returns %value. +llvm::Value *CodeGenFunction::EmitARCStoreWeak(llvm::Value *addr, + llvm::Value *value, + bool ignored) { + return emitARCStoreOperation(*this, addr, value, + CGM.getARCEntrypoints().objc_storeWeak, + "objc_storeWeak", ignored); +} + +/// i8* @objc_initWeak(i8** %addr, i8* %value) +/// Returns %value. %addr is known to not have a current weak entry. +/// Essentially equivalent to: +/// *addr = nil; objc_storeWeak(addr, value); +void CodeGenFunction::EmitARCInitWeak(llvm::Value *addr, llvm::Value *value) { + // If we're initializing to null, just write null to memory; no need + // to get the runtime involved. But don't do this if optimization + // is enabled, because accounting for this would make the optimizer + // much more complicated. + if (isa<llvm::ConstantPointerNull>(value) && + CGM.getCodeGenOpts().OptimizationLevel == 0) { + Builder.CreateStore(value, addr); + return; + } + + emitARCStoreOperation(*this, addr, value, + CGM.getARCEntrypoints().objc_initWeak, + "objc_initWeak", /*ignored*/ true); +} + +/// void @objc_destroyWeak(i8** %addr) +/// Essentially objc_storeWeak(addr, nil). +void CodeGenFunction::EmitARCDestroyWeak(llvm::Value *addr) { + llvm::Constant *&fn = CGM.getARCEntrypoints().objc_destroyWeak; + if (!fn) { + std::vector<llvm::Type*> args(1, Int8PtrPtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), args, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_destroyWeak"); + } + + // Cast the argument to 'id*'. + addr = Builder.CreateBitCast(addr, Int8PtrPtrTy); + + llvm::CallInst *call = Builder.CreateCall(fn, addr); + call->setDoesNotThrow(); +} + +/// void @objc_moveWeak(i8** %dest, i8** %src) +/// Disregards the current value in %dest. Leaves %src pointing to nothing. +/// Essentially (objc_copyWeak(dest, src), objc_destroyWeak(src)). +void CodeGenFunction::EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src) { + emitARCCopyOperation(*this, dst, src, + CGM.getARCEntrypoints().objc_moveWeak, + "objc_moveWeak"); +} + +/// void @objc_copyWeak(i8** %dest, i8** %src) +/// Disregards the current value in %dest. Essentially +/// objc_release(objc_initWeak(dest, objc_readWeakRetained(src))) +void CodeGenFunction::EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src) { + emitARCCopyOperation(*this, dst, src, + CGM.getARCEntrypoints().objc_copyWeak, + "objc_copyWeak"); +} + +/// Produce the code to do a objc_autoreleasepool_push. +/// call i8* @objc_autoreleasePoolPush(void) +llvm::Value *CodeGenFunction::EmitObjCAutoreleasePoolPush() { + llvm::Constant *&fn = CGM.getRREntrypoints().objc_autoreleasePoolPush; + if (!fn) { + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Int8PtrTy, false); + fn = createARCRuntimeFunction(CGM, fnType, "objc_autoreleasePoolPush"); + } + + llvm::CallInst *call = Builder.CreateCall(fn); + call->setDoesNotThrow(); + + return call; +} + +/// Produce the code to do a primitive release. +/// call void @objc_autoreleasePoolPop(i8* %ptr) +void CodeGenFunction::EmitObjCAutoreleasePoolPop(llvm::Value *value) { + assert(value->getType() == Int8PtrTy); + + llvm::Constant *&fn = CGM.getRREntrypoints().objc_autoreleasePoolPop; + if (!fn) { + std::vector<llvm::Type*> args(1, Int8PtrTy); + const llvm::FunctionType *fnType = + llvm::FunctionType::get(Builder.getVoidTy(), args, false); + + // We don't want to use a weak import here; instead we should not + // fall into this path. + fn = createARCRuntimeFunction(CGM, fnType, "objc_autoreleasePoolPop"); + } + + llvm::CallInst *call = Builder.CreateCall(fn, value); + call->setDoesNotThrow(); +} + +/// Produce the code to do an MRR version objc_autoreleasepool_push. +/// Which is: [[NSAutoreleasePool alloc] init]; +/// Where alloc is declared as: + (id) alloc; in NSAutoreleasePool class. +/// init is declared as: - (id) init; in its NSObject super class. +/// +llvm::Value *CodeGenFunction::EmitObjCMRRAutoreleasePoolPush() { + CGObjCRuntime &Runtime = CGM.getObjCRuntime(); + llvm::Value *Receiver = Runtime.EmitNSAutoreleasePoolClassRef(Builder); + // [NSAutoreleasePool alloc] + IdentifierInfo *II = &CGM.getContext().Idents.get("alloc"); + Selector AllocSel = getContext().Selectors.getSelector(0, &II); + CallArgList Args; + RValue AllocRV = + Runtime.GenerateMessageSend(*this, ReturnValueSlot(), + getContext().getObjCIdType(), + AllocSel, Receiver, Args); + + // [Receiver init] + Receiver = AllocRV.getScalarVal(); + II = &CGM.getContext().Idents.get("init"); + Selector InitSel = getContext().Selectors.getSelector(0, &II); + RValue InitRV = + Runtime.GenerateMessageSend(*this, ReturnValueSlot(), + getContext().getObjCIdType(), + InitSel, Receiver, Args); + return InitRV.getScalarVal(); +} + +/// Produce the code to do a primitive release. +/// [tmp drain]; +void CodeGenFunction::EmitObjCMRRAutoreleasePoolPop(llvm::Value *Arg) { + IdentifierInfo *II = &CGM.getContext().Idents.get("drain"); + Selector DrainSel = getContext().Selectors.getSelector(0, &II); + CallArgList Args; + CGM.getObjCRuntime().GenerateMessageSend(*this, ReturnValueSlot(), + getContext().VoidTy, DrainSel, Arg, Args); +} + +void CodeGenFunction::destroyARCStrongPrecise(CodeGenFunction &CGF, + llvm::Value *addr, + QualType type) { + llvm::Value *ptr = CGF.Builder.CreateLoad(addr, "strongdestroy"); + CGF.EmitARCRelease(ptr, /*precise*/ true); +} + +void CodeGenFunction::destroyARCStrongImprecise(CodeGenFunction &CGF, + llvm::Value *addr, + QualType type) { + llvm::Value *ptr = CGF.Builder.CreateLoad(addr, "strongdestroy"); + CGF.EmitARCRelease(ptr, /*precise*/ false); +} + +void CodeGenFunction::destroyARCWeak(CodeGenFunction &CGF, + llvm::Value *addr, + QualType type) { + CGF.EmitARCDestroyWeak(addr); +} + +namespace { + struct CallObjCAutoreleasePoolObject : EHScopeStack::Cleanup { + llvm::Value *Token; + + CallObjCAutoreleasePoolObject(llvm::Value *token) : Token(token) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + CGF.EmitObjCAutoreleasePoolPop(Token); + } + }; + struct CallObjCMRRAutoreleasePoolObject : EHScopeStack::Cleanup { + llvm::Value *Token; + + CallObjCMRRAutoreleasePoolObject(llvm::Value *token) : Token(token) {} + + void Emit(CodeGenFunction &CGF, Flags flags) { + CGF.EmitObjCMRRAutoreleasePoolPop(Token); + } + }; +} + +void CodeGenFunction::EmitObjCAutoreleasePoolCleanup(llvm::Value *Ptr) { + if (CGM.getLangOptions().ObjCAutoRefCount) + EHStack.pushCleanup<CallObjCAutoreleasePoolObject>(NormalCleanup, Ptr); + else + EHStack.pushCleanup<CallObjCMRRAutoreleasePoolObject>(NormalCleanup, Ptr); +} + +static TryEmitResult tryEmitARCRetainLoadOfScalar(CodeGenFunction &CGF, + LValue lvalue, + QualType type) { + switch (type.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + return TryEmitResult(CGF.EmitLoadOfLValue(lvalue).getScalarVal(), + false); + + case Qualifiers::OCL_Weak: + return TryEmitResult(CGF.EmitARCLoadWeakRetained(lvalue.getAddress()), + true); + } + + llvm_unreachable("impossible lifetime!"); + return TryEmitResult(); +} + +static TryEmitResult tryEmitARCRetainLoadOfScalar(CodeGenFunction &CGF, + const Expr *e) { + e = e->IgnoreParens(); + QualType type = e->getType(); + + // As a very special optimization, in ARC++, if the l-value is the + // result of a non-volatile assignment, do a simple retain of the + // result of the call to objc_storeWeak instead of reloading. + if (CGF.getLangOptions().CPlusPlus && + !type.isVolatileQualified() && + type.getObjCLifetime() == Qualifiers::OCL_Weak && + isa<BinaryOperator>(e) && + cast<BinaryOperator>(e)->getOpcode() == BO_Assign) + return TryEmitResult(CGF.EmitScalarExpr(e), false); + + return tryEmitARCRetainLoadOfScalar(CGF, CGF.EmitLValue(e), type); +} + +static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, + llvm::Value *value); + +/// Given that the given expression is some sort of call (which does +/// not return retained), emit a retain following it. +static llvm::Value *emitARCRetainCall(CodeGenFunction &CGF, const Expr *e) { + llvm::Value *value = CGF.EmitScalarExpr(e); + return emitARCRetainAfterCall(CGF, value); +} + +static llvm::Value *emitARCRetainAfterCall(CodeGenFunction &CGF, + llvm::Value *value) { + if (llvm::CallInst *call = dyn_cast<llvm::CallInst>(value)) { + CGBuilderTy::InsertPoint ip = CGF.Builder.saveIP(); + + // Place the retain immediately following the call. + CGF.Builder.SetInsertPoint(call->getParent(), + ++llvm::BasicBlock::iterator(call)); + value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + + CGF.Builder.restoreIP(ip); + return value; + } else if (llvm::InvokeInst *invoke = dyn_cast<llvm::InvokeInst>(value)) { + CGBuilderTy::InsertPoint ip = CGF.Builder.saveIP(); + + // Place the retain at the beginning of the normal destination block. + llvm::BasicBlock *BB = invoke->getNormalDest(); + CGF.Builder.SetInsertPoint(BB, BB->begin()); + value = CGF.EmitARCRetainAutoreleasedReturnValue(value); + + CGF.Builder.restoreIP(ip); + return value; + + // Bitcasts can arise because of related-result returns. Rewrite + // the operand. + } else if (llvm::BitCastInst *bitcast = dyn_cast<llvm::BitCastInst>(value)) { + llvm::Value *operand = bitcast->getOperand(0); + operand = emitARCRetainAfterCall(CGF, operand); + bitcast->setOperand(0, operand); + return bitcast; + + // Generic fall-back case. + } else { + // Retain using the non-block variant: we never need to do a copy + // of a block that's been returned to us. + return CGF.EmitARCRetainNonBlock(value); + } +} + +static TryEmitResult +tryEmitARCRetainScalarExpr(CodeGenFunction &CGF, const Expr *e) { + // The desired result type, if it differs from the type of the + // ultimate opaque expression. + const llvm::Type *resultType = 0; + + // If we're loading retained from a __strong xvalue, we can avoid + // an extra retain/release pair by zeroing out the source of this + // "move" operation. + if (e->isXValue() && !e->getType().isConstQualified() && + e->getType().getObjCLifetime() == Qualifiers::OCL_Strong) { + // Emit the lvalue + LValue lv = CGF.EmitLValue(e); + + // Load the object pointer and cast it to the appropriate type. + QualType exprType = e->getType(); + llvm::Value *result = CGF.EmitLoadOfLValue(lv).getScalarVal(); + + if (resultType) + result = CGF.Builder.CreateBitCast(result, resultType); + + // Set the source pointer to NULL. + llvm::Value *null + = llvm::ConstantPointerNull::get( + cast<llvm::PointerType>(CGF.ConvertType(exprType))); + CGF.EmitStoreOfScalar(null, lv); + + return TryEmitResult(result, true); + } + + while (true) { + e = e->IgnoreParens(); + + // There's a break at the end of this if-chain; anything + // that wants to keep looping has to explicitly continue. + if (const CastExpr *ce = dyn_cast<CastExpr>(e)) { + switch (ce->getCastKind()) { + // No-op casts don't change the type, so we just ignore them. + case CK_NoOp: + e = ce->getSubExpr(); + continue; + + case CK_LValueToRValue: { + TryEmitResult loadResult + = tryEmitARCRetainLoadOfScalar(CGF, ce->getSubExpr()); + if (resultType) { + llvm::Value *value = loadResult.getPointer(); + value = CGF.Builder.CreateBitCast(value, resultType); + loadResult.setPointer(value); + } + return loadResult; + } + + // These casts can change the type, so remember that and + // soldier on. We only need to remember the outermost such + // cast, though. + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_BitCast: + if (!resultType) + resultType = CGF.ConvertType(ce->getType()); + e = ce->getSubExpr(); + assert(e->getType()->hasPointerRepresentation()); + continue; + + // For consumptions, just emit the subexpression and thus elide + // the retain/release pair. + case CK_ObjCConsumeObject: { + llvm::Value *result = CGF.EmitScalarExpr(ce->getSubExpr()); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + // For reclaims, emit the subexpression as a retained call and + // skip the consumption. + case CK_ObjCReclaimReturnedObject: { + llvm::Value *result = emitARCRetainCall(CGF, ce->getSubExpr()); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + case CK_GetObjCProperty: { + llvm::Value *result = emitARCRetainCall(CGF, ce); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + default: + break; + } + + // Skip __extension__. + } else if (const UnaryOperator *op = dyn_cast<UnaryOperator>(e)) { + if (op->getOpcode() == UO_Extension) { + e = op->getSubExpr(); + continue; + } + + // For calls and message sends, use the retained-call logic. + // Delegate inits are a special case in that they're the only + // returns-retained expression that *isn't* surrounded by + // a consume. + } else if (isa<CallExpr>(e) || + (isa<ObjCMessageExpr>(e) && + !cast<ObjCMessageExpr>(e)->isDelegateInitCall())) { + llvm::Value *result = emitARCRetainCall(CGF, e); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, true); + } + + // Conservatively halt the search at any other expression kind. + break; + } + + // We didn't find an obvious production, so emit what we've got and + // tell the caller that we didn't manage to retain. + llvm::Value *result = CGF.EmitScalarExpr(e); + if (resultType) result = CGF.Builder.CreateBitCast(result, resultType); + return TryEmitResult(result, false); +} + +static llvm::Value *emitARCRetainLoadOfScalar(CodeGenFunction &CGF, + LValue lvalue, + QualType type) { + TryEmitResult result = tryEmitARCRetainLoadOfScalar(CGF, lvalue, type); + llvm::Value *value = result.getPointer(); + if (!result.getInt()) + value = CGF.EmitARCRetain(type, value); + return value; +} + +/// EmitARCRetainScalarExpr - Semantically equivalent to +/// EmitARCRetainObject(e->getType(), EmitScalarExpr(e)), but making a +/// best-effort attempt to peephole expressions that naturally produce +/// retained objects. +llvm::Value *CodeGenFunction::EmitARCRetainScalarExpr(const Expr *e) { + TryEmitResult result = tryEmitARCRetainScalarExpr(*this, e); + llvm::Value *value = result.getPointer(); + if (!result.getInt()) + value = EmitARCRetain(e->getType(), value); + return value; +} + +llvm::Value * +CodeGenFunction::EmitARCRetainAutoreleaseScalarExpr(const Expr *e) { + TryEmitResult result = tryEmitARCRetainScalarExpr(*this, e); + llvm::Value *value = result.getPointer(); + if (result.getInt()) + value = EmitARCAutorelease(value); + else + value = EmitARCRetainAutorelease(e->getType(), value); + return value; +} + +std::pair<LValue,llvm::Value*> +CodeGenFunction::EmitARCStoreStrong(const BinaryOperator *e, + bool ignored) { + // Evaluate the RHS first. + TryEmitResult result = tryEmitARCRetainScalarExpr(*this, e->getRHS()); + llvm::Value *value = result.getPointer(); + + LValue lvalue = EmitLValue(e->getLHS()); + + // If the RHS was emitted retained, expand this. + if (result.getInt()) { + llvm::Value *oldValue = + EmitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatileQualified(), + lvalue.getAlignment(), e->getType(), + lvalue.getTBAAInfo()); + EmitStoreOfScalar(value, lvalue.getAddress(), + lvalue.isVolatileQualified(), lvalue.getAlignment(), + e->getType(), lvalue.getTBAAInfo()); + EmitARCRelease(oldValue, /*precise*/ false); + } else { + value = EmitARCStoreStrong(lvalue, value, ignored); + } + + return std::pair<LValue,llvm::Value*>(lvalue, value); +} + +std::pair<LValue,llvm::Value*> +CodeGenFunction::EmitARCStoreAutoreleasing(const BinaryOperator *e) { + llvm::Value *value = EmitARCRetainAutoreleaseScalarExpr(e->getRHS()); + LValue lvalue = EmitLValue(e->getLHS()); + + EmitStoreOfScalar(value, lvalue.getAddress(), + lvalue.isVolatileQualified(), lvalue.getAlignment(), + e->getType(), lvalue.getTBAAInfo()); + + return std::pair<LValue,llvm::Value*>(lvalue, value); +} + +void CodeGenFunction::EmitObjCAutoreleasePoolStmt( + const ObjCAutoreleasePoolStmt &ARPS) { + const Stmt *subStmt = ARPS.getSubStmt(); + const CompoundStmt &S = cast<CompoundStmt>(*subStmt); + + CGDebugInfo *DI = getDebugInfo(); + if (DI) { + DI->setLocation(S.getLBracLoc()); + DI->EmitRegionStart(Builder); + } + + // Keep track of the current cleanup stack depth. + RunCleanupsScope Scope(*this); + if (CGM.getCodeGenOpts().ObjCRuntimeHasARC) { + llvm::Value *token = EmitObjCAutoreleasePoolPush(); + EHStack.pushCleanup<CallObjCAutoreleasePoolObject>(NormalCleanup, token); + } else { + llvm::Value *token = EmitObjCMRRAutoreleasePoolPush(); + EHStack.pushCleanup<CallObjCMRRAutoreleasePoolObject>(NormalCleanup, token); + } + + for (CompoundStmt::const_body_iterator I = S.body_begin(), + E = S.body_end(); I != E; ++I) + EmitStmt(*I); + + if (DI) { + DI->setLocation(S.getRBracLoc()); + DI->EmitRegionEnd(Builder); + } +} + +/// EmitExtendGCLifetime - Given a pointer to an Objective-C object, +/// make sure it survives garbage collection until this point. +void CodeGenFunction::EmitExtendGCLifetime(llvm::Value *object) { + // We just use an inline assembly. + llvm::Type *paramTypes[] = { VoidPtrTy }; + llvm::FunctionType *extenderType + = llvm::FunctionType::get(VoidTy, paramTypes, /*variadic*/ false); + llvm::Value *extender + = llvm::InlineAsm::get(extenderType, + /* assembly */ "", + /* constraints */ "r", + /* side effects */ true); + + object = Builder.CreateBitCast(object, VoidPtrTy); + Builder.CreateCall(extender, object)->setDoesNotThrow(); +} + CGObjCRuntime::~CGObjCRuntime() {} diff --git a/lib/CodeGen/CGObjCGNU.cpp b/lib/CodeGen/CGObjCGNU.cpp index f0993c5dadbb..61027feb5cb9 100644 --- a/lib/CodeGen/CGObjCGNU.cpp +++ b/lib/CodeGen/CGObjCGNU.cpp @@ -50,7 +50,7 @@ namespace { /// avoids constructing the type more than once if it's used more than once. class LazyRuntimeFunction { CodeGenModule *CGM; - std::vector<const llvm::Type*> ArgTys; + std::vector<llvm::Type*> ArgTys; const char *FunctionName; llvm::Constant *Function; public: @@ -63,14 +63,14 @@ class LazyRuntimeFunction { /// of the arguments. END_WITH_NULL void init(CodeGenModule *Mod, const char *name, - const llvm::Type *RetTy, ...) { + llvm::Type *RetTy, ...) { CGM =Mod; FunctionName = name; Function = 0; ArgTys.clear(); va_list Args; va_start(Args, RetTy); - while (const llvm::Type *ArgTy = va_arg(Args, const llvm::Type*)) + while (llvm::Type *ArgTy = va_arg(Args, llvm::Type*)) ArgTys.push_back(ArgTy); va_end(Args); // Push the return type on at the end so we can pop it off easily @@ -118,24 +118,24 @@ protected: /// LLVM type for selectors. Opaque pointer (i8*) unless a header declaring /// SEL is included in a header somewhere, in which case it will be whatever /// type is declared in that header, most likely {i8*, i8*}. - const llvm::PointerType *SelectorTy; + llvm::PointerType *SelectorTy; /// LLVM i8 type. Cached here to avoid repeatedly getting it in all of the /// places where it's used const llvm::IntegerType *Int8Ty; /// Pointer to i8 - LLVM type of char*, for all of the places where the /// runtime needs to deal with C strings. - const llvm::PointerType *PtrToInt8Ty; + llvm::PointerType *PtrToInt8Ty; /// Instance Method Pointer type. This is a pointer to a function that takes, /// at a minimum, an object and a selector, and is the generic type for /// Objective-C methods. Due to differences between variadic / non-variadic /// calling conventions, it must always be cast to the correct type before /// actually being used. - const llvm::PointerType *IMPTy; + llvm::PointerType *IMPTy; /// Type of an untyped Objective-C object. Clang treats id as a built-in type /// when compiling Objective-C code, so this may be an opaque pointer (i8*), /// but if the runtime header declaring it is included then it may be a /// pointer to a structure. - const llvm::PointerType *IdTy; + llvm::PointerType *IdTy; /// Pointer to a pointer to an Objective-C object. Used in the new ABI /// message lookup function and some GC-related functions. const llvm::PointerType *PtrToIdTy; @@ -143,15 +143,15 @@ protected: /// call Objective-C methods. CanQualType ASTIdTy; /// LLVM type for C int type. - const llvm::IntegerType *IntTy; + llvm::IntegerType *IntTy; /// LLVM type for an opaque pointer. This is identical to PtrToInt8Ty, but is /// used in the code to document the difference between i8* meaning a pointer /// to a C string and i8* meaning a pointer to some opaque type. - const llvm::PointerType *PtrTy; + llvm::PointerType *PtrTy; /// LLVM type for C long type. The runtime uses this in a lot of places where /// it should be using intptr_t, but we can't fix this without breaking /// compatibility with GCC... - const llvm::IntegerType *LongTy; + llvm::IntegerType *LongTy; /// LLVM type for C size_t. Used in various runtime data structures. const llvm::IntegerType *SizeTy; /// LLVM type for C ptrdiff_t. Mainly used in property accessor functions. @@ -392,6 +392,9 @@ private: /// Emits a reference to a class. This allows the linker to object if there /// is no class of the matching name. void EmitClassRef(const std::string &className); + /// Emits a pointer to the named class + llvm::Value *GetClassNamed(CGBuilderTy &Builder, const std::string &Name, + bool isWeak); protected: /// Looks up the method for sending a message to the specified object. This /// mechanism differs between the GCC and GNU runtimes, so this method must be @@ -484,6 +487,7 @@ public: virtual llvm::Value *EmitIvarOffset(CodeGenFunction &CGF, const ObjCInterfaceDecl *Interface, const ObjCIvarDecl *Ivar); + virtual llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder); virtual llvm::Constant *BuildGCBlockLayout(CodeGenModule &CGM, const CGBlockInfo &blockInfo) { return NULLPtr; @@ -527,7 +531,7 @@ protected: CGBuilderTy &Builder = CGF.Builder; llvm::Value *lookupArgs[] = {EnforceType(Builder, ObjCSuper, PtrToObjCSuperTy), cmd}; - return Builder.CreateCall(MsgLookupSuperFn, lookupArgs, lookupArgs+2); + return Builder.CreateCall(MsgLookupSuperFn, lookupArgs); } public: CGObjCGCC(CodeGenModule &Mod) : CGObjCGNU(Mod, 8, 2) { @@ -596,15 +600,14 @@ class CGObjCGNUstep : public CGObjCGNU { CGBuilderTy &Builder = CGF.Builder; llvm::Value *lookupArgs[] = {ObjCSuper, cmd}; - llvm::CallInst *slot = Builder.CreateCall(SlotLookupSuperFn, lookupArgs, - lookupArgs+2); + llvm::CallInst *slot = Builder.CreateCall(SlotLookupSuperFn, lookupArgs); slot->setOnlyReadsMemory(); return Builder.CreateLoad(Builder.CreateStructGEP(slot, 4)); } public: CGObjCGNUstep(CodeGenModule &Mod) : CGObjCGNU(Mod, 9, 3) { - llvm::StructType *SlotStructTy = llvm::StructType::get(VMContext, PtrTy, + llvm::StructType *SlotStructTy = llvm::StructType::get(PtrTy, PtrTy, PtrTy, IntTy, IMPTy, NULL); SlotTy = llvm::PointerType::getUnqual(SlotStructTy); // Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender); @@ -615,7 +618,7 @@ class CGObjCGNUstep : public CGObjCGNU { PtrToObjCSuperTy, SelectorTy, NULL); // If we're in ObjC++ mode, then we want to make if (CGM.getLangOptions().CPlusPlus) { - const llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext); + llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext); // void *__cxa_begin_catch(void *e) EnterCatchFn.init(&CGM, "__cxa_begin_catch", PtrTy, PtrTy, NULL); // void __cxa_end_catch(void) @@ -705,10 +708,10 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion, } PtrToIdTy = llvm::PointerType::getUnqual(IdTy); - ObjCSuperTy = llvm::StructType::get(VMContext, IdTy, IdTy, NULL); + ObjCSuperTy = llvm::StructType::get(IdTy, IdTy, NULL); PtrToObjCSuperTy = llvm::PointerType::getUnqual(ObjCSuperTy); - const llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext); + llvm::Type *VoidTy = llvm::Type::getVoidTy(VMContext); // void objc_exception_throw(id); ExceptionThrowFn.init(&CGM, "objc_exception_throw", VoidTy, IdTy, NULL); @@ -736,16 +739,19 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion, PtrDiffTy, BoolTy, BoolTy, NULL); // IMP type - const llvm::Type *IMPArgs[] = { IdTy, SelectorTy }; + llvm::Type *IMPArgs[] = { IdTy, SelectorTy }; IMPTy = llvm::PointerType::getUnqual(llvm::FunctionType::get(IdTy, IMPArgs, true)); + const LangOptions &Opts = CGM.getLangOptions(); + if ((Opts.getGCMode() != LangOptions::NonGC) || Opts.ObjCAutoRefCount) + RuntimeVersion = 10; + // Don't bother initialising the GC stuff unless we're compiling in GC mode - if (CGM.getLangOptions().getGCMode() != LangOptions::NonGC) { + if (Opts.getGCMode() != LangOptions::NonGC) { // This is a bit of an hack. We should sort this out by having a proper // CGObjCGNUstep subclass for GC, but we may want to really support the old // ABI and GC added in ObjectiveC2.framework, so we fudge it a bit for now - RuntimeVersion = 10; // Get selectors needed in GC mode RetainSel = GetNullarySelector("retain", CGM.getContext()); ReleaseSel = GetNullarySelector("release", CGM.getContext()); @@ -772,24 +778,38 @@ CGObjCGNU::CGObjCGNU(CodeGenModule &cgm, unsigned runtimeABIVersion, } } -// This has to perform the lookup every time, since posing and related -// techniques can modify the name -> class mapping. -llvm::Value *CGObjCGNU::GetClass(CGBuilderTy &Builder, - const ObjCInterfaceDecl *OID) { - llvm::Value *ClassName = CGM.GetAddrOfConstantCString(OID->getNameAsString()); +llvm::Value *CGObjCGNU::GetClassNamed(CGBuilderTy &Builder, + const std::string &Name, + bool isWeak) { + llvm::Value *ClassName = CGM.GetAddrOfConstantCString(Name); // With the incompatible ABI, this will need to be replaced with a direct // reference to the class symbol. For the compatible nonfragile ABI we are // still performing this lookup at run time but emitting the symbol for the // class externally so that we can make the switch later. - EmitClassRef(OID->getNameAsString()); + // + // Libobjc2 contains an LLVM pass that replaces calls to objc_lookup_class + // with memoized versions or with static references if it's safe to do so. + if (!isWeak) + EmitClassRef(Name); ClassName = Builder.CreateStructGEP(ClassName, 0); + llvm::Type *ArgTys[] = { PtrToInt8Ty }; llvm::Constant *ClassLookupFn = - CGM.CreateRuntimeFunction(llvm::FunctionType::get(IdTy, PtrToInt8Ty, true), + CGM.CreateRuntimeFunction(llvm::FunctionType::get(IdTy, ArgTys, true), "objc_lookup_class"); return Builder.CreateCall(ClassLookupFn, ClassName); } +// This has to perform the lookup every time, since posing and related +// techniques can modify the name -> class mapping. +llvm::Value *CGObjCGNU::GetClass(CGBuilderTy &Builder, + const ObjCInterfaceDecl *OID) { + return GetClassNamed(Builder, OID->getNameAsString(), OID->isWeakImported()); +} +llvm::Value *CGObjCGNU::EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder) { + return GetClassNamed(Builder, "NSAutoreleasePool", false); +} + llvm::Value *CGObjCGNU::GetSelector(CGBuilderTy &Builder, Selector Sel, const std::string &TypeEncoding, bool lval) { @@ -908,7 +928,7 @@ llvm::Constant *CGObjCGNU::GetEHType(QualType T) { fields.push_back(Vtable); fields.push_back(typeName); llvm::Constant *TI = - MakeGlobal(llvm::StructType::get(VMContext, PtrToInt8Ty, PtrToInt8Ty, + MakeGlobal(llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, NULL), fields, "__objc_eh_typeinfo_" + className, llvm::GlobalValue::LinkOnceODRLinkage); return llvm::ConstantExpr::getBitCast(TI, PtrToInt8Ty); @@ -929,7 +949,7 @@ llvm::Constant *CGObjCGNU::GenerateConstantString(const StringLiteral *SL) { Ivars.push_back(MakeConstantString(Str)); Ivars.push_back(llvm::ConstantInt::get(IntTy, Str.size())); llvm::Constant *ObjCStr = MakeGlobal( - llvm::StructType::get(VMContext, PtrToInt8Ty, PtrToInt8Ty, IntTy, NULL), + llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, IntTy, NULL), Ivars, ".objc_str"); ObjCStr = llvm::ConstantExpr::getBitCast(ObjCStr, PtrToInt8Ty); ObjCStrings[Str] = ObjCStr; @@ -969,7 +989,7 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, ActualArgs.add(RValue::get(EnforceType(Builder, Receiver, IdTy)), ASTIdTy); ActualArgs.add(RValue::get(cmd), CGF.getContext().getObjCSelType()); - ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); + ActualArgs.addFrom(CallArgs); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, @@ -979,11 +999,13 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, if (isCategoryImpl) { llvm::Constant *classLookupFunction = 0; if (IsClassMessage) { + llvm::Type *ArgTys[] = { PtrTy }; classLookupFunction = CGM.CreateRuntimeFunction(llvm::FunctionType::get( - IdTy, PtrTy, true), "objc_get_meta_class"); + IdTy, ArgTys, true), "objc_get_meta_class"); } else { + llvm::Type *ArgTys[] = { PtrTy }; classLookupFunction = CGM.CreateRuntimeFunction(llvm::FunctionType::get( - IdTy, PtrTy, true), "objc_get_class"); + IdTy, ArgTys, true), "objc_get_class"); } ReceiverClass = Builder.CreateCall(classLookupFunction, MakeConstantString(Class->getNameAsString())); @@ -1012,13 +1034,13 @@ CGObjCGNU::GenerateMessageSendSuper(CodeGenFunction &CGF, // Cast the pointer to a simplified version of the class structure ReceiverClass = Builder.CreateBitCast(ReceiverClass, llvm::PointerType::getUnqual( - llvm::StructType::get(VMContext, IdTy, IdTy, NULL))); + llvm::StructType::get(IdTy, IdTy, NULL))); // Get the superclass pointer ReceiverClass = Builder.CreateStructGEP(ReceiverClass, 1); // Load the superclass pointer ReceiverClass = Builder.CreateLoad(ReceiverClass); // Construct the structure used to look up the IMP - llvm::StructType *ObjCSuperTy = llvm::StructType::get(VMContext, + llvm::StructType *ObjCSuperTy = llvm::StructType::get( Receiver->getType(), IdTy, NULL); llvm::Value *ObjCSuper = Builder.CreateAlloca(ObjCSuperTy); @@ -1121,7 +1143,7 @@ CGObjCGNU::GenerateMessageSend(CodeGenFunction &CGF, CallArgList ActualArgs; ActualArgs.add(RValue::get(Receiver), ASTIdTy); ActualArgs.add(RValue::get(cmd), CGF.getContext().getObjCSelType()); - ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); + ActualArgs.addFrom(CallArgs); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, @@ -1187,7 +1209,7 @@ llvm::Constant *CGObjCGNU::GenerateMethodList(const llvm::StringRef &ClassName, if (MethodSels.empty()) return NULLPtr; // Get the method structure type. - llvm::StructType *ObjCMethodTy = llvm::StructType::get(VMContext, + llvm::StructType *ObjCMethodTy = llvm::StructType::get( PtrToInt8Ty, // Really a selector, but the runtime creates it us. PtrToInt8Ty, // Method types IMPTy, //Method pointer @@ -1217,18 +1239,14 @@ llvm::Constant *CGObjCGNU::GenerateMethodList(const llvm::StringRef &ClassName, Methods); // Structure containing list pointer, array and array count - llvm::SmallVector<const llvm::Type*, 16> ObjCMethodListFields; - llvm::PATypeHolder OpaqueNextTy = llvm::OpaqueType::get(VMContext); - llvm::Type *NextPtrTy = llvm::PointerType::getUnqual(OpaqueNextTy); - llvm::StructType *ObjCMethodListTy = llvm::StructType::get(VMContext, + llvm::StructType *ObjCMethodListTy = + llvm::StructType::createNamed(VMContext, ""); + llvm::Type *NextPtrTy = llvm::PointerType::getUnqual(ObjCMethodListTy); + ObjCMethodListTy->setBody( NextPtrTy, IntTy, ObjCMethodArrayTy, NULL); - // Refine next pointer type to concrete type - llvm::cast<llvm::OpaqueType>( - OpaqueNextTy.get())->refineAbstractTypeTo(ObjCMethodListTy); - ObjCMethodListTy = llvm::cast<llvm::StructType>(OpaqueNextTy.get()); Methods.clear(); Methods.push_back(llvm::ConstantPointerNull::get( @@ -1249,7 +1267,7 @@ llvm::Constant *CGObjCGNU::GenerateIvarList( if (IvarNames.size() == 0) return NULLPtr; // Get the method structure type. - llvm::StructType *ObjCIvarTy = llvm::StructType::get(VMContext, + llvm::StructType *ObjCIvarTy = llvm::StructType::get( PtrToInt8Ty, PtrToInt8Ty, IntTy, @@ -1273,7 +1291,7 @@ llvm::Constant *CGObjCGNU::GenerateIvarList( Elements.push_back(llvm::ConstantInt::get(IntTy, (int)IvarNames.size())); Elements.push_back(llvm::ConstantArray::get(ObjCIvarArrayTy, Ivars)); // Structure containing array and array count - llvm::StructType *ObjCIvarListTy = llvm::StructType::get(VMContext, IntTy, + llvm::StructType *ObjCIvarListTy = llvm::StructType::get(IntTy, ObjCIvarArrayTy, NULL); @@ -1302,7 +1320,7 @@ llvm::Constant *CGObjCGNU::GenerateClassStructure( // Fields marked New ABI are part of the GNUstep runtime. We emit them // anyway; the classes will still work with the GNU runtime, they will just // be ignored. - llvm::StructType *ClassTy = llvm::StructType::get(VMContext, + llvm::StructType *ClassTy = llvm::StructType::get( PtrToInt8Ty, // class_pointer PtrToInt8Ty, // super_class PtrToInt8Ty, // name @@ -1359,7 +1377,7 @@ llvm::Constant *CGObjCGNU::GenerateProtocolMethodList( const llvm::SmallVectorImpl<llvm::Constant *> &MethodNames, const llvm::SmallVectorImpl<llvm::Constant *> &MethodTypes) { // Get the method structure type. - llvm::StructType *ObjCMethodDescTy = llvm::StructType::get(VMContext, + llvm::StructType *ObjCMethodDescTy = llvm::StructType::get( PtrToInt8Ty, // Really a selector, but the runtime does the casting for us. PtrToInt8Ty, NULL); @@ -1375,7 +1393,7 @@ llvm::Constant *CGObjCGNU::GenerateProtocolMethodList( MethodNames.size()); llvm::Constant *Array = llvm::ConstantArray::get(ObjCMethodArrayTy, Methods); - llvm::StructType *ObjCMethodDescListTy = llvm::StructType::get(VMContext, + llvm::StructType *ObjCMethodDescListTy = llvm::StructType::get( IntTy, ObjCMethodArrayTy, NULL); Methods.clear(); Methods.push_back(llvm::ConstantInt::get(IntTy, MethodNames.size())); @@ -1388,7 +1406,7 @@ llvm::Constant *CGObjCGNU::GenerateProtocolList( const llvm::SmallVectorImpl<std::string> &Protocols) { llvm::ArrayType *ProtocolArrayTy = llvm::ArrayType::get(PtrToInt8Ty, Protocols.size()); - llvm::StructType *ProtocolListTy = llvm::StructType::get(VMContext, + llvm::StructType *ProtocolListTy = llvm::StructType::get( PtrTy, //Should be a recurisve pointer, but it's always NULL here. SizeTy, ProtocolArrayTy, @@ -1435,7 +1453,7 @@ llvm::Constant *CGObjCGNU::GenerateEmptyProtocol( GenerateProtocolMethodList(EmptyConstantVector, EmptyConstantVector); // Protocols are objects containing lists of the methods implemented and // protocols adopted. - llvm::StructType *ProtocolTy = llvm::StructType::get(VMContext, IdTy, + llvm::StructType *ProtocolTy = llvm::StructType::get(IdTy, PtrToInt8Ty, ProtocolList->getType(), MethodList->getType(), @@ -1521,7 +1539,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { // The isSynthesized value is always set to 0 in a protocol. It exists to // simplify the runtime library by allowing it to use the same data // structures for protocol metadata everywhere. - llvm::StructType *PropertyMetadataTy = llvm::StructType::get(VMContext, + llvm::StructType *PropertyMetadataTy = llvm::StructType::get( PtrToInt8Ty, Int8Ty, Int8Ty, PtrToInt8Ty, PtrToInt8Ty, PtrToInt8Ty, PtrToInt8Ty, NULL); std::vector<llvm::Constant*> Properties; @@ -1573,7 +1591,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { {llvm::ConstantInt::get(IntTy, Properties.size()), NULLPtr, PropertyArray}; llvm::Constant *PropertyListInit = - llvm::ConstantStruct::get(VMContext, PropertyListInitFields, 3, false); + llvm::ConstantStruct::getAnon(PropertyListInitFields); llvm::Constant *PropertyList = new llvm::GlobalVariable(TheModule, PropertyListInit->getType(), false, llvm::GlobalValue::InternalLinkage, PropertyListInit, ".objc_property_list"); @@ -1586,7 +1604,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { OptionalPropertyArray }; llvm::Constant *OptionalPropertyListInit = - llvm::ConstantStruct::get(VMContext, OptionalPropertyListInitFields, 3, false); + llvm::ConstantStruct::getAnon(OptionalPropertyListInitFields); llvm::Constant *OptionalPropertyList = new llvm::GlobalVariable(TheModule, OptionalPropertyListInit->getType(), false, llvm::GlobalValue::InternalLinkage, OptionalPropertyListInit, @@ -1594,7 +1612,7 @@ void CGObjCGNU::GenerateProtocol(const ObjCProtocolDecl *PD) { // Protocols are objects containing lists of the methods implemented and // protocols adopted. - llvm::StructType *ProtocolTy = llvm::StructType::get(VMContext, IdTy, + llvm::StructType *ProtocolTy = llvm::StructType::get(IdTy, PtrToInt8Ty, ProtocolList->getType(), InstanceMethodList->getType(), @@ -1641,7 +1659,7 @@ void CGObjCGNU::GenerateProtocolHolderCategory(void) { // Protocol list llvm::ArrayType *ProtocolArrayTy = llvm::ArrayType::get(PtrTy, ExistingProtocols.size()); - llvm::StructType *ProtocolListTy = llvm::StructType::get(VMContext, + llvm::StructType *ProtocolListTy = llvm::StructType::get( PtrTy, //Should be a recurisve pointer, but it's always NULL here. SizeTy, ProtocolArrayTy, @@ -1664,7 +1682,7 @@ void CGObjCGNU::GenerateProtocolHolderCategory(void) { Elements.push_back(llvm::ConstantExpr::getBitCast(MakeGlobal(ProtocolListTy, ProtocolElements, ".objc_protocol_list"), PtrTy)); Categories.push_back(llvm::ConstantExpr::getBitCast( - MakeGlobal(llvm::StructType::get(VMContext, PtrToInt8Ty, PtrToInt8Ty, + MakeGlobal(llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, PtrTy, PtrTy, PtrTy, NULL), Elements), PtrTy)); } @@ -1718,7 +1736,7 @@ void CGObjCGNU::GenerateCategory(const ObjCCategoryImplDecl *OCD) { Elements.push_back(llvm::ConstantExpr::getBitCast( GenerateProtocolList(Protocols), PtrTy)); Categories.push_back(llvm::ConstantExpr::getBitCast( - MakeGlobal(llvm::StructType::get(VMContext, PtrToInt8Ty, PtrToInt8Ty, + MakeGlobal(llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, PtrTy, PtrTy, PtrTy, NULL), Elements), PtrTy)); } @@ -1729,7 +1747,7 @@ llvm::Constant *CGObjCGNU::GeneratePropertyList(const ObjCImplementationDecl *OI // // Property metadata: name, attributes, isSynthesized, setter name, setter // types, getter name, getter types. - llvm::StructType *PropertyMetadataTy = llvm::StructType::get(VMContext, + llvm::StructType *PropertyMetadataTy = llvm::StructType::get( PtrToInt8Ty, Int8Ty, Int8Ty, PtrToInt8Ty, PtrToInt8Ty, PtrToInt8Ty, PtrToInt8Ty, NULL); std::vector<llvm::Constant*> Properties; @@ -1788,7 +1806,7 @@ llvm::Constant *CGObjCGNU::GeneratePropertyList(const ObjCImplementationDecl *OI {llvm::ConstantInt::get(IntTy, Properties.size()), NULLPtr, PropertyArray}; llvm::Constant *PropertyListInit = - llvm::ConstantStruct::get(VMContext, PropertyListInitFields, 3, false); + llvm::ConstantStruct::getAnon(PropertyListInitFields); return new llvm::GlobalVariable(TheModule, PropertyListInit->getType(), false, llvm::GlobalValue::InternalLinkage, PropertyListInit, ".objc_property_list"); @@ -1859,13 +1877,25 @@ void CGObjCGNU::GenerateClass(const ObjCImplementationDecl *OID) { if (CGM.getContext().getLangOptions().ObjCNonFragileABI) { Offset = BaseOffset - superInstanceSize; } - IvarOffsets.push_back( - llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), Offset)); - IvarOffsetValues.push_back(new llvm::GlobalVariable(TheModule, IntTy, + llvm::Constant *OffsetValue = llvm::ConstantInt::get(IntTy, Offset); + // Create the direct offset value + std::string OffsetName = "__objc_ivar_offset_value_" + ClassName +"." + + IVD->getNameAsString(); + llvm::GlobalVariable *OffsetVar = TheModule.getGlobalVariable(OffsetName); + if (OffsetVar) { + OffsetVar->setInitializer(OffsetValue); + // If this is the real definition, change its linkage type so that + // different modules will use this one, rather than their private + // copy. + OffsetVar->setLinkage(llvm::GlobalValue::ExternalLinkage); + } else + OffsetVar = new llvm::GlobalVariable(TheModule, IntTy, false, llvm::GlobalValue::ExternalLinkage, - llvm::ConstantInt::get(IntTy, Offset), + OffsetValue, "__objc_ivar_offset_value_" + ClassName +"." + - IVD->getNameAsString())); + IVD->getNameAsString()); + IvarOffsets.push_back(OffsetValue); + IvarOffsetValues.push_back(OffsetVar); } llvm::GlobalVariable *IvarOffsetArray = MakeGlobalArray(PtrToIntTy, IvarOffsetValues, ".ivar.offsets"); @@ -2005,18 +2035,12 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { const llvm::StructType *SelStructTy = dyn_cast<llvm::StructType>( SelectorTy->getElementType()); - const llvm::Type *SelStructPtrTy = SelectorTy; + llvm::Type *SelStructPtrTy = SelectorTy; if (SelStructTy == 0) { - SelStructTy = llvm::StructType::get(VMContext, PtrToInt8Ty, - PtrToInt8Ty, NULL); + SelStructTy = llvm::StructType::get(PtrToInt8Ty, PtrToInt8Ty, NULL); SelStructPtrTy = llvm::PointerType::getUnqual(SelStructTy); } - // Name the ObjC types to make the IR a bit easier to read - TheModule.addTypeName(".objc_selector", SelStructPtrTy); - TheModule.addTypeName(".objc_id", IdTy); - TheModule.addTypeName(".objc_imp", IMPTy); - std::vector<llvm::Constant*> Elements; llvm::Constant *Statics = NULLPtr; // Generate statics list: @@ -2034,7 +2058,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { Elements.push_back(llvm::ConstantArray::get(StaticsArrayTy, ConstantStrings)); llvm::StructType *StaticsListTy = - llvm::StructType::get(VMContext, PtrToInt8Ty, StaticsArrayTy, NULL); + llvm::StructType::get(PtrToInt8Ty, StaticsArrayTy, NULL); llvm::Type *StaticsListPtrTy = llvm::PointerType::getUnqual(StaticsListTy); Statics = MakeGlobal(StaticsListTy, Elements, ".objc_statics"); @@ -2049,8 +2073,7 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { // Array of classes, categories, and constant objects llvm::ArrayType *ClassListTy = llvm::ArrayType::get(PtrToInt8Ty, Classes.size() + Categories.size() + 2); - llvm::StructType *SymTabTy = llvm::StructType::get(VMContext, - LongTy, SelStructPtrTy, + llvm::StructType *SymTabTy = llvm::StructType::get(LongTy, SelStructPtrTy, llvm::Type::getInt16Ty(VMContext), llvm::Type::getInt16Ty(VMContext), ClassListTy, NULL); @@ -2132,10 +2155,9 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { // The symbol table is contained in a module which has some version-checking // constants - llvm::StructType * ModuleTy = llvm::StructType::get(VMContext, LongTy, LongTy, + llvm::StructType * ModuleTy = llvm::StructType::get(LongTy, LongTy, PtrToInt8Ty, llvm::PointerType::getUnqual(SymTabTy), - (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) ? NULL : IntTy, - NULL); + (RuntimeVersion >= 10) ? IntTy : NULL, NULL); Elements.clear(); // Runtime version, used for ABI compatibility checking. Elements.push_back(llvm::ConstantInt::get(LongTy, RuntimeVersion)); @@ -2154,14 +2176,21 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { Elements.push_back(MakeConstantString(path, ".objc_source_file_name")); Elements.push_back(SymTab); - switch (CGM.getLangOptions().getGCMode()) { - case LangOptions::GCOnly: + if (RuntimeVersion >= 10) + switch (CGM.getLangOptions().getGCMode()) { + case LangOptions::GCOnly: Elements.push_back(llvm::ConstantInt::get(IntTy, 2)); - case LangOptions::NonGC: break; - case LangOptions::HybridGC: - Elements.push_back(llvm::ConstantInt::get(IntTy, 1)); - } + case LangOptions::NonGC: + if (CGM.getLangOptions().ObjCAutoRefCount) + Elements.push_back(llvm::ConstantInt::get(IntTy, 1)); + else + Elements.push_back(llvm::ConstantInt::get(IntTy, 0)); + break; + case LangOptions::HybridGC: + Elements.push_back(llvm::ConstantInt::get(IntTy, 1)); + break; + } llvm::Value *Module = MakeGlobal(ModuleTy, Elements); @@ -2176,9 +2205,9 @@ llvm::Function *CGObjCGNU::ModuleInitFunction() { CGBuilderTy Builder(VMContext); Builder.SetInsertPoint(EntryBB); + llvm::Type *ArgTys[] = { llvm::PointerType::getUnqual(ModuleTy) }; llvm::FunctionType *FT = - llvm::FunctionType::get(Builder.getVoidTy(), - llvm::PointerType::getUnqual(ModuleTy), true); + llvm::FunctionType::get(Builder.getVoidTy(), ArgTys, true); llvm::Value *Register = CGM.CreateRuntimeFunction(FT, "__objc_exec_class"); Builder.CreateCall(Register, Module); Builder.CreateRetVoid(); @@ -2280,8 +2309,8 @@ void CGObjCGNU::EmitThrowStmt(CodeGenFunction &CGF, CGF.Builder.CreateCall(ExceptionThrowFn, ExceptionAsObject); CGF.Builder.CreateUnreachable(); } else { - CGF.Builder.CreateInvoke(ExceptionThrowFn, UnwindBB, UnwindBB, &ExceptionAsObject, - &ExceptionAsObject+1); + CGF.Builder.CreateInvoke(ExceptionThrowFn, UnwindBB, UnwindBB, + ExceptionAsObject); } // Clear the insertion point to indicate we are in unreachable code. CGF.Builder.ClearInsertionPoint(); @@ -2422,10 +2451,19 @@ llvm::Value *CGObjCGNU::EmitIvarOffset(CodeGenFunction &CGF, const ObjCIvarDecl *Ivar) { if (CGM.getLangOptions().ObjCNonFragileABI) { Interface = FindIvarInterface(CGM.getContext(), Interface, Ivar); - return CGF.Builder.CreateZExtOrBitCast( - CGF.Builder.CreateLoad(CGF.Builder.CreateLoad( - ObjCIvarOffsetVariable(Interface, Ivar), false, "ivar")), - PtrDiffTy); + if (RuntimeVersion < 10) + return CGF.Builder.CreateZExtOrBitCast( + CGF.Builder.CreateLoad(CGF.Builder.CreateLoad( + ObjCIvarOffsetVariable(Interface, Ivar), false, "ivar")), + PtrDiffTy); + std::string name = "__objc_ivar_offset_value_" + + Interface->getNameAsString() +"." + Ivar->getNameAsString(); + llvm::Value *Offset = TheModule.getGlobalVariable(name); + if (!Offset) + Offset = new llvm::GlobalVariable(TheModule, IntTy, + false, llvm::GlobalValue::CommonLinkage, + 0, name); + return CGF.Builder.CreateLoad(Offset); } uint64_t Offset = ComputeIvarBaseOffset(CGF.CGM, Interface, Ivar); return llvm::ConstantInt::get(PtrDiffTy, Offset, "ivar"); diff --git a/lib/CodeGen/CGObjCMac.cpp b/lib/CodeGen/CGObjCMac.cpp index 8c3e9a36e307..010b9e174e46 100644 --- a/lib/CodeGen/CGObjCMac.cpp +++ b/lib/CodeGen/CGObjCMac.cpp @@ -63,10 +63,13 @@ private: /// The default messenger, used for sends whose ABI is unchanged from /// the all-integer/pointer case. llvm::Constant *getMessageSendFn() const { - const llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; + // Add the non-lazy-bind attribute, since objc_msgSend is likely to + // be called a lot. + llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), - "objc_msgSend"); + "objc_msgSend", + llvm::Attribute::NonLazyBind); } /// void objc_msgSend_stret (id, SEL, ...) @@ -75,7 +78,7 @@ private: /// by indirect reference in the first argument, and therefore the /// self and selector parameters are shifted over by one. llvm::Constant *getMessageSendStretFn() const { - const llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.VoidTy, params, true), "objc_msgSend_stret"); @@ -88,7 +91,7 @@ private: /// floating-point stack; without a special entrypoint, the nil case /// would be unbalanced. llvm::Constant *getMessageSendFpretFn() const { - const llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get( llvm::Type::getDoubleTy(VMContext), params, true), @@ -102,7 +105,7 @@ private: /// semantics. The class passed is the superclass of the current /// class. llvm::Constant *getMessageSendSuperFn() const { - const llvm::Type *params[] = { SuperPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { SuperPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSendSuper"); @@ -113,7 +116,7 @@ private: /// A slightly different messenger used for super calls. The class /// passed is the current class. llvm::Constant *getMessageSendSuperFn2() const { - const llvm::Type *params[] = { SuperPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { SuperPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSendSuper2"); @@ -124,7 +127,7 @@ private: /// /// The messenger used for super calls which return an aggregate indirectly. llvm::Constant *getMessageSendSuperStretFn() const { - const llvm::Type *params[] = { Int8PtrTy, SuperPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { Int8PtrTy, SuperPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction( llvm::FunctionType::get(CGM.VoidTy, params, true), "objc_msgSendSuper_stret"); @@ -135,7 +138,7 @@ private: /// /// objc_msgSendSuper_stret with the super2 semantics. llvm::Constant *getMessageSendSuperStretFn2() const { - const llvm::Type *params[] = { Int8PtrTy, SuperPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { Int8PtrTy, SuperPtrTy, SelectorPtrTy }; return CGM.CreateRuntimeFunction( llvm::FunctionType::get(CGM.VoidTy, params, true), "objc_msgSendSuper2_stret"); @@ -155,20 +158,20 @@ protected: CodeGen::CodeGenModule &CGM; public: - const llvm::Type *ShortTy, *IntTy, *LongTy, *LongLongTy; - const llvm::Type *Int8PtrTy; + llvm::Type *ShortTy, *IntTy, *LongTy, *LongLongTy; + llvm::Type *Int8PtrTy; /// ObjectPtrTy - LLVM type for object handles (typeof(id)) - const llvm::Type *ObjectPtrTy; + llvm::Type *ObjectPtrTy; /// PtrObjectPtrTy - LLVM type for id * - const llvm::Type *PtrObjectPtrTy; + llvm::Type *PtrObjectPtrTy; /// SelectorPtrTy - LLVM type for selector handles (typeof(SEL)) - const llvm::Type *SelectorPtrTy; + llvm::Type *SelectorPtrTy; /// ProtocolPtrTy - LLVM type for external protocol handles /// (typeof(Protocol)) - const llvm::Type *ExternalProtocolPtrTy; + llvm::Type *ExternalProtocolPtrTy; // SuperCTy - clang type for struct objc_super. QualType SuperCTy; @@ -176,28 +179,28 @@ public: QualType SuperPtrCTy; /// SuperTy - LLVM type for struct objc_super. - const llvm::StructType *SuperTy; + llvm::StructType *SuperTy; /// SuperPtrTy - LLVM type for struct objc_super *. - const llvm::Type *SuperPtrTy; + llvm::Type *SuperPtrTy; /// PropertyTy - LLVM type for struct objc_property (struct _prop_t /// in GCC parlance). - const llvm::StructType *PropertyTy; + llvm::StructType *PropertyTy; /// PropertyListTy - LLVM type for struct objc_property_list /// (_prop_list_t in GCC parlance). - const llvm::StructType *PropertyListTy; + llvm::StructType *PropertyListTy; /// PropertyListPtrTy - LLVM type for struct objc_property_list*. - const llvm::Type *PropertyListPtrTy; + llvm::Type *PropertyListPtrTy; // MethodTy - LLVM type for struct objc_method. - const llvm::StructType *MethodTy; + llvm::StructType *MethodTy; /// CacheTy - LLVM type for struct objc_cache. - const llvm::Type *CacheTy; + llvm::Type *CacheTy; /// CachePtrTy - LLVM type for struct objc_cache *. - const llvm::Type *CachePtrTy; - + llvm::Type *CachePtrTy; + llvm::Constant *getGetPropertyFn() { CodeGen::CodeGenTypes &Types = CGM.getTypes(); ASTContext &Ctx = CGM.getContext(); @@ -270,7 +273,7 @@ public: /// GcReadWeakFn -- LLVM objc_read_weak (id *src) function. llvm::Constant *getGcReadWeakFn() { // id objc_read_weak (id *) - const llvm::Type *args[] = { ObjectPtrTy->getPointerTo() }; + llvm::Type *args[] = { ObjectPtrTy->getPointerTo() }; llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_read_weak"); @@ -279,7 +282,7 @@ public: /// GcAssignWeakFn -- LLVM objc_assign_weak function. llvm::Constant *getGcAssignWeakFn() { // id objc_assign_weak (id, id *) - const llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; + llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_weak"); @@ -288,7 +291,7 @@ public: /// GcAssignGlobalFn -- LLVM objc_assign_global function. llvm::Constant *getGcAssignGlobalFn() { // id objc_assign_global(id, id *) - const llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; + llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_global"); @@ -297,7 +300,7 @@ public: /// GcAssignThreadLocalFn -- LLVM objc_assign_threadlocal function. llvm::Constant *getGcAssignThreadLocalFn() { // id objc_assign_threadlocal(id src, id * dest) - const llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; + llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_threadlocal"); @@ -306,8 +309,8 @@ public: /// GcAssignIvarFn -- LLVM objc_assign_ivar function. llvm::Constant *getGcAssignIvarFn() { // id objc_assign_ivar(id, id *, ptrdiff_t) - const llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo(), - CGM.PtrDiffTy }; + llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo(), + CGM.PtrDiffTy }; llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_ivar"); @@ -316,7 +319,7 @@ public: /// GcMemmoveCollectableFn -- LLVM objc_memmove_collectable function. llvm::Constant *GcMemmoveCollectableFn() { // void *objc_memmove_collectable(void *dst, const void *src, size_t size) - const llvm::Type *args[] = { Int8PtrTy, Int8PtrTy, LongTy }; + llvm::Type *args[] = { Int8PtrTy, Int8PtrTy, LongTy }; llvm::FunctionType *FTy = llvm::FunctionType::get(Int8PtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_memmove_collectable"); } @@ -324,7 +327,7 @@ public: /// GcAssignStrongCastFn -- LLVM objc_assign_strongCast function. llvm::Constant *getGcAssignStrongCastFn() { // id objc_assign_strongCast(id, id *) - const llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; + llvm::Type *args[] = { ObjectPtrTy, ObjectPtrTy->getPointerTo() }; llvm::FunctionType *FTy = llvm::FunctionType::get(ObjectPtrTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_assign_strongCast"); @@ -333,7 +336,7 @@ public: /// ExceptionThrowFn - LLVM objc_exception_throw function. llvm::Constant *getExceptionThrowFn() { // void objc_exception_throw(id) - const llvm::Type *args[] = { ObjectPtrTy }; + llvm::Type *args[] = { ObjectPtrTy }; llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_exception_throw"); @@ -349,7 +352,7 @@ public: /// SyncEnterFn - LLVM object_sync_enter function. llvm::Constant *getSyncEnterFn() { // void objc_sync_enter (id) - const llvm::Type *args[] = { ObjectPtrTy }; + llvm::Type *args[] = { ObjectPtrTy }; llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_sync_enter"); @@ -358,7 +361,7 @@ public: /// SyncExitFn - LLVM object_sync_exit function. llvm::Constant *getSyncExitFn() { // void objc_sync_exit (id) - const llvm::Type *args[] = { ObjectPtrTy }; + llvm::Type *args[] = { ObjectPtrTy }; llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.VoidTy, args, false); return CGM.CreateRuntimeFunction(FTy, "objc_sync_exit"); @@ -397,62 +400,62 @@ public: class ObjCTypesHelper : public ObjCCommonTypesHelper { public: /// SymtabTy - LLVM type for struct objc_symtab. - const llvm::StructType *SymtabTy; + llvm::StructType *SymtabTy; /// SymtabPtrTy - LLVM type for struct objc_symtab *. - const llvm::Type *SymtabPtrTy; + llvm::Type *SymtabPtrTy; /// ModuleTy - LLVM type for struct objc_module. - const llvm::StructType *ModuleTy; + llvm::StructType *ModuleTy; /// ProtocolTy - LLVM type for struct objc_protocol. - const llvm::StructType *ProtocolTy; + llvm::StructType *ProtocolTy; /// ProtocolPtrTy - LLVM type for struct objc_protocol *. - const llvm::Type *ProtocolPtrTy; + llvm::Type *ProtocolPtrTy; /// ProtocolExtensionTy - LLVM type for struct /// objc_protocol_extension. - const llvm::StructType *ProtocolExtensionTy; + llvm::StructType *ProtocolExtensionTy; /// ProtocolExtensionTy - LLVM type for struct /// objc_protocol_extension *. - const llvm::Type *ProtocolExtensionPtrTy; + llvm::Type *ProtocolExtensionPtrTy; /// MethodDescriptionTy - LLVM type for struct /// objc_method_description. - const llvm::StructType *MethodDescriptionTy; + llvm::StructType *MethodDescriptionTy; /// MethodDescriptionListTy - LLVM type for struct /// objc_method_description_list. - const llvm::StructType *MethodDescriptionListTy; + llvm::StructType *MethodDescriptionListTy; /// MethodDescriptionListPtrTy - LLVM type for struct /// objc_method_description_list *. - const llvm::Type *MethodDescriptionListPtrTy; + llvm::Type *MethodDescriptionListPtrTy; /// ProtocolListTy - LLVM type for struct objc_property_list. - const llvm::Type *ProtocolListTy; + llvm::StructType *ProtocolListTy; /// ProtocolListPtrTy - LLVM type for struct objc_property_list*. - const llvm::Type *ProtocolListPtrTy; + llvm::Type *ProtocolListPtrTy; /// CategoryTy - LLVM type for struct objc_category. - const llvm::StructType *CategoryTy; + llvm::StructType *CategoryTy; /// ClassTy - LLVM type for struct objc_class. - const llvm::StructType *ClassTy; + llvm::StructType *ClassTy; /// ClassPtrTy - LLVM type for struct objc_class *. - const llvm::Type *ClassPtrTy; + llvm::Type *ClassPtrTy; /// ClassExtensionTy - LLVM type for struct objc_class_ext. - const llvm::StructType *ClassExtensionTy; + llvm::StructType *ClassExtensionTy; /// ClassExtensionPtrTy - LLVM type for struct objc_class_ext *. - const llvm::Type *ClassExtensionPtrTy; + llvm::Type *ClassExtensionPtrTy; // IvarTy - LLVM type for struct objc_ivar. - const llvm::StructType *IvarTy; + llvm::StructType *IvarTy; /// IvarListTy - LLVM type for struct objc_ivar_list. - const llvm::Type *IvarListTy; + llvm::Type *IvarListTy; /// IvarListPtrTy - LLVM type for struct objc_ivar_list *. - const llvm::Type *IvarListPtrTy; + llvm::Type *IvarListPtrTy; /// MethodListTy - LLVM type for struct objc_method_list. - const llvm::Type *MethodListTy; + llvm::Type *MethodListTy; /// MethodListPtrTy - LLVM type for struct objc_method_list *. - const llvm::Type *MethodListPtrTy; + llvm::Type *MethodListPtrTy; /// ExceptionDataTy - LLVM type for struct _objc_exception_data. - const llvm::Type *ExceptionDataTy; - + llvm::Type *ExceptionDataTy; + /// ExceptionTryEnterFn - LLVM objc_exception_try_enter function. llvm::Constant *getExceptionTryEnterFn() { - const llvm::Type *params[] = { ExceptionDataTy->getPointerTo() }; + llvm::Type *params[] = { ExceptionDataTy->getPointerTo() }; return CGM.CreateRuntimeFunction( llvm::FunctionType::get(CGM.VoidTy, params, false), "objc_exception_try_enter"); @@ -460,7 +463,7 @@ public: /// ExceptionTryExitFn - LLVM objc_exception_try_exit function. llvm::Constant *getExceptionTryExitFn() { - const llvm::Type *params[] = { ExceptionDataTy->getPointerTo() }; + llvm::Type *params[] = { ExceptionDataTy->getPointerTo() }; return CGM.CreateRuntimeFunction( llvm::FunctionType::get(CGM.VoidTy, params, false), "objc_exception_try_exit"); @@ -468,7 +471,7 @@ public: /// ExceptionExtractFn - LLVM objc_exception_extract function. llvm::Constant *getExceptionExtractFn() { - const llvm::Type *params[] = { ExceptionDataTy->getPointerTo() }; + llvm::Type *params[] = { ExceptionDataTy->getPointerTo() }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, false), "objc_exception_extract"); @@ -476,7 +479,7 @@ public: /// ExceptionMatchFn - LLVM objc_exception_match function. llvm::Constant *getExceptionMatchFn() { - const llvm::Type *params[] = { ClassPtrTy, ObjectPtrTy }; + llvm::Type *params[] = { ClassPtrTy, ObjectPtrTy }; return CGM.CreateRuntimeFunction( llvm::FunctionType::get(CGM.Int32Ty, params, false), "objc_exception_match"); @@ -486,7 +489,7 @@ public: /// SetJmpFn - LLVM _setjmp function. llvm::Constant *getSetJmpFn() { // This is specifically the prototype for x86. - const llvm::Type *params[] = { CGM.Int32Ty->getPointerTo() }; + llvm::Type *params[] = { CGM.Int32Ty->getPointerTo() }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(CGM.Int32Ty, params, false), "_setjmp"); @@ -503,46 +506,46 @@ class ObjCNonFragileABITypesHelper : public ObjCCommonTypesHelper { public: // MethodListnfABITy - LLVM for struct _method_list_t - const llvm::StructType *MethodListnfABITy; + llvm::StructType *MethodListnfABITy; // MethodListnfABIPtrTy - LLVM for struct _method_list_t* - const llvm::Type *MethodListnfABIPtrTy; + llvm::Type *MethodListnfABIPtrTy; // ProtocolnfABITy = LLVM for struct _protocol_t - const llvm::StructType *ProtocolnfABITy; + llvm::StructType *ProtocolnfABITy; // ProtocolnfABIPtrTy = LLVM for struct _protocol_t* - const llvm::Type *ProtocolnfABIPtrTy; + llvm::Type *ProtocolnfABIPtrTy; // ProtocolListnfABITy - LLVM for struct _objc_protocol_list - const llvm::StructType *ProtocolListnfABITy; + llvm::StructType *ProtocolListnfABITy; // ProtocolListnfABIPtrTy - LLVM for struct _objc_protocol_list* - const llvm::Type *ProtocolListnfABIPtrTy; + llvm::Type *ProtocolListnfABIPtrTy; // ClassnfABITy - LLVM for struct _class_t - const llvm::StructType *ClassnfABITy; + llvm::StructType *ClassnfABITy; // ClassnfABIPtrTy - LLVM for struct _class_t* - const llvm::Type *ClassnfABIPtrTy; + llvm::Type *ClassnfABIPtrTy; // IvarnfABITy - LLVM for struct _ivar_t - const llvm::StructType *IvarnfABITy; + llvm::StructType *IvarnfABITy; // IvarListnfABITy - LLVM for struct _ivar_list_t - const llvm::StructType *IvarListnfABITy; + llvm::StructType *IvarListnfABITy; // IvarListnfABIPtrTy = LLVM for struct _ivar_list_t* - const llvm::Type *IvarListnfABIPtrTy; + llvm::Type *IvarListnfABIPtrTy; // ClassRonfABITy - LLVM for struct _class_ro_t - const llvm::StructType *ClassRonfABITy; + llvm::StructType *ClassRonfABITy; // ImpnfABITy - LLVM for id (*)(id, SEL, ...) - const llvm::Type *ImpnfABITy; + llvm::Type *ImpnfABITy; // CategorynfABITy - LLVM for struct _category_t - const llvm::StructType *CategorynfABITy; + llvm::StructType *CategorynfABITy; // New types for nonfragile abi messaging. @@ -551,31 +554,31 @@ public: // IMP messenger; // SEL name; // }; - const llvm::StructType *MessageRefTy; + llvm::StructType *MessageRefTy; // MessageRefCTy - clang type for struct _message_ref_t QualType MessageRefCTy; // MessageRefPtrTy - LLVM for struct _message_ref_t* - const llvm::Type *MessageRefPtrTy; + llvm::Type *MessageRefPtrTy; // MessageRefCPtrTy - clang type for struct _message_ref_t* QualType MessageRefCPtrTy; // MessengerTy - Type of the messenger (shown as IMP above) - const llvm::FunctionType *MessengerTy; + llvm::FunctionType *MessengerTy; // SuperMessageRefTy - LLVM for: // struct _super_message_ref_t { // SUPER_IMP messenger; // SEL name; // }; - const llvm::StructType *SuperMessageRefTy; + llvm::StructType *SuperMessageRefTy; // SuperMessageRefPtrTy - LLVM for struct _super_message_ref_t* - const llvm::Type *SuperMessageRefPtrTy; + llvm::Type *SuperMessageRefPtrTy; llvm::Constant *getMessageSendFixupFn() { // id objc_msgSend_fixup(id, struct message_ref_t*, ...) - const llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy }; + llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSend_fixup"); @@ -583,7 +586,7 @@ public: llvm::Constant *getMessageSendFpretFixupFn() { // id objc_msgSend_fpret_fixup(id, struct message_ref_t*, ...) - const llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy }; + llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSend_fpret_fixup"); @@ -591,7 +594,7 @@ public: llvm::Constant *getMessageSendStretFixupFn() { // id objc_msgSend_stret_fixup(id, struct message_ref_t*, ...) - const llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy }; + llvm::Type *params[] = { ObjectPtrTy, MessageRefPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSend_stret_fixup"); @@ -600,7 +603,7 @@ public: llvm::Constant *getMessageSendSuper2FixupFn() { // id objc_msgSendSuper2_fixup (struct objc_super *, // struct _super_message_ref_t*, ...) - const llvm::Type *params[] = { SuperPtrTy, SuperMessageRefPtrTy }; + llvm::Type *params[] = { SuperPtrTy, SuperMessageRefPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSendSuper2_fixup"); @@ -609,7 +612,7 @@ public: llvm::Constant *getMessageSendSuper2StretFixupFn() { // id objc_msgSendSuper2_stret_fixup(struct objc_super *, // struct _super_message_ref_t*, ...) - const llvm::Type *params[] = { SuperPtrTy, SuperMessageRefPtrTy }; + llvm::Type *params[] = { SuperPtrTy, SuperMessageRefPtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(ObjectPtrTy, params, true), "objc_msgSendSuper2_stret_fixup"); @@ -622,15 +625,15 @@ public: } llvm::Constant *getObjCBeginCatchFn() { - const llvm::Type *params[] = { Int8PtrTy }; + llvm::Type *params[] = { Int8PtrTy }; return CGM.CreateRuntimeFunction(llvm::FunctionType::get(Int8PtrTy, params, false), "objc_begin_catch"); } - const llvm::StructType *EHTypeTy; - const llvm::Type *EHTypePtrTy; - + llvm::StructType *EHTypeTy; + llvm::Type *EHTypePtrTy; + ObjCNonFragileABITypesHelper(CodeGen::CodeGenModule &cgm); ~ObjCNonFragileABITypesHelper(){} }; @@ -887,6 +890,11 @@ private: llvm::Value *EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); + llvm::Value *EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II); + + llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder); + /// EmitSuperClassRef - Emits reference to class's main metadata class. llvm::Value *EmitSuperClassRef(const ObjCInterfaceDecl *ID); @@ -1158,6 +1166,11 @@ private: /// for the given class reference. llvm::Value *EmitClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID); + + llvm::Value *EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II); + + llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder); /// EmitSuperClassRef - Return a Value*, of type ObjCTypes.ClassPtrTy, /// for the given super class reference. @@ -1402,6 +1415,19 @@ llvm::Value *CGObjCMac::GetSelector(CGBuilderTy &Builder, const ObjCMethodDecl } llvm::Constant *CGObjCMac::GetEHType(QualType T) { + if (T->isObjCIdType() || + T->isObjCQualifiedIdType()) { + return CGM.GetAddrOfRTTIDescriptor( + CGM.getContext().ObjCIdRedefinitionType, /*ForEH=*/true); + } + if (T->isObjCClassType() || + T->isObjCQualifiedClassType()) { + return CGM.GetAddrOfRTTIDescriptor( + CGM.getContext().ObjCClassRedefinitionType, /*ForEH=*/true); + } + if (T->isObjCObjectPointerType()) + return CGM.GetAddrOfRTTIDescriptor(T, /*ForEH=*/true); + llvm_unreachable("asking for catch type for ObjC type in fragile runtime"); return 0; } @@ -1526,7 +1552,7 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, Arg0 = CGF.Builder.CreateBitCast(Arg0, ObjCTypes.ObjectPtrTy, "tmp"); ActualArgs.add(RValue::get(Arg0), Arg0Ty); ActualArgs.add(RValue::get(Sel), CGF.getContext().getObjCSelType()); - ActualArgs.insert(ActualArgs.end(), CallArgs.begin(), CallArgs.end()); + ActualArgs.addFrom(CallArgs); CodeGenTypes &Types = CGM.getTypes(); const CGFunctionInfo &FnInfo = Types.getFunctionInfo(ResultType, ActualArgs, @@ -1562,7 +1588,7 @@ static Qualifiers::GC GetGCAttrTypeForType(ASTContext &Ctx, QualType FQT) { if (FQT.isObjCGCStrong()) return Qualifiers::Strong; - if (FQT.isObjCGCWeak()) + if (FQT.isObjCGCWeak() || FQT.getObjCLifetime() == Qualifiers::OCL_Weak) return Qualifiers::Weak; if (FQT->isObjCObjectPointerType() || FQT->isBlockPointerType()) @@ -1579,7 +1605,8 @@ llvm::Constant *CGObjCCommonMac::BuildGCBlockLayout(CodeGenModule &CGM, llvm::Constant *nullPtr = llvm::Constant::getNullValue(llvm::Type::getInt8PtrTy(VMContext)); - if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) + if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC && + !CGM.getLangOptions().ObjCAutoRefCount) return nullPtr; bool hasUnion = false; @@ -1866,7 +1893,7 @@ CGObjCMac::EmitProtocolList(llvm::Twine Name, // This list is null terminated. ProtocolRefs.push_back(llvm::Constant::getNullValue(ObjCTypes.ProtocolPtrTy)); - std::vector<llvm::Constant*> Values(3); + llvm::Constant *Values[3]; // This field is only used by the runtime. Values[0] = llvm::Constant::getNullValue(ObjCTypes.ProtocolListPtrTy); Values[1] = llvm::ConstantInt::get(ObjCTypes.LongTy, @@ -1876,7 +1903,7 @@ CGObjCMac::EmitProtocolList(llvm::Twine Name, ProtocolRefs.size()), ProtocolRefs); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, "__OBJC,__cat_cls_meth,regular,no_dead_strip", 4, false); @@ -1950,13 +1977,13 @@ llvm::Constant *CGObjCCommonMac::EmitPropertyList(llvm::Twine Name, unsigned PropertySize = CGM.getTargetData().getTypeAllocSize(ObjCTypes.PropertyTy); - std::vector<llvm::Constant*> Values(3); + llvm::Constant *Values[3]; Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, PropertySize); Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Properties.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.PropertyTy, Properties.size()); Values[2] = llvm::ConstantArray::get(AT, Properties); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, @@ -1994,12 +2021,12 @@ llvm::Constant *CGObjCMac::EmitMethodDescList(llvm::Twine Name, if (Methods.empty()) return llvm::Constant::getNullValue(ObjCTypes.MethodDescriptionListPtrTy); - std::vector<llvm::Constant*> Values(2); + llvm::Constant *Values[2]; Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Methods.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.MethodDescriptionTy, Methods.size()); Values[1] = llvm::ConstantArray::get(AT, Methods); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, Section, 4, true); return llvm::ConstantExpr::getBitCast(GV, @@ -2044,7 +2071,7 @@ void CGObjCMac::GenerateCategory(const ObjCCategoryImplDecl *OCD) { ClassMethods.push_back(GetMethodConstant(*i)); } - std::vector<llvm::Constant*> Values(7); + llvm::Constant *Values[7]; Values[0] = GetClassName(OCD->getIdentifier()); Values[1] = GetClassName(Interface->getIdentifier()); LazySymbols.insert(Interface->getIdentifier()); @@ -2129,7 +2156,7 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) { Interface->all_referenced_protocol_begin(), Interface->all_referenced_protocol_end()); unsigned Flags = eClassFlags_Factory; - if (ID->getNumIvarInitializers()) + if (ID->hasCXXStructors()) Flags |= eClassFlags_HasCXXStructors; unsigned Size = CGM.getContext().getASTObjCImplementationLayout(ID).getSize().getQuantity(); @@ -2166,7 +2193,7 @@ void CGObjCMac::GenerateClass(const ObjCImplementationDecl *ID) { } } - std::vector<llvm::Constant*> Values(12); + llvm::Constant *Values[12]; Values[ 0] = EmitMetaClass(ID, Protocols, ClassMethods); if (ObjCInterfaceDecl *Super = Interface->getSuperClass()) { // Record a reference to the super class. @@ -2225,7 +2252,7 @@ llvm::Constant *CGObjCMac::EmitMetaClass(const ObjCImplementationDecl *ID, if (ID->getClassInterface()->getVisibility() == HiddenVisibility) Flags |= eClassFlags_Hidden; - std::vector<llvm::Constant*> Values(12); + llvm::Constant *Values[12]; // The isa for the metaclass is the root of the hierarchy. const ObjCInterfaceDecl *Root = ID->getClassInterface(); while (const ObjCInterfaceDecl *Super = Root->getSuperClass()) @@ -2339,7 +2366,7 @@ CGObjCMac::EmitClassExtension(const ObjCImplementationDecl *ID) { uint64_t Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.ClassExtensionTy); - std::vector<llvm::Constant*> Values(3); + llvm::Constant *Values[3]; Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Values[1] = BuildIvarLayout(ID, false); Values[2] = EmitPropertyList("\01l_OBJC_$_PROP_LIST_" + ID->getName(), @@ -2383,11 +2410,8 @@ llvm::Constant *CGObjCMac::EmitIvarList(const ObjCImplementationDecl *ID, ObjCInterfaceDecl *OID = const_cast<ObjCInterfaceDecl*>(ID->getClassInterface()); - llvm::SmallVector<ObjCIvarDecl*, 16> OIvars; - CGM.getContext().ShallowCollectObjCIvars(OID, OIvars); - - for (unsigned i = 0, e = OIvars.size(); i != e; ++i) { - ObjCIvarDecl *IVD = OIvars[i]; + for (ObjCIvarDecl *IVD = OID->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { // Ignore unnamed bit-fields. if (!IVD->getDeclName()) continue; @@ -2402,12 +2426,12 @@ llvm::Constant *CGObjCMac::EmitIvarList(const ObjCImplementationDecl *ID, if (Ivars.empty()) return llvm::Constant::getNullValue(ObjCTypes.IvarListPtrTy); - std::vector<llvm::Constant*> Values(2); + llvm::Constant *Values[2]; Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Ivars.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.IvarTy, Ivars.size()); Values[1] = llvm::ConstantArray::get(AT, Ivars); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV; if (ForClass) @@ -2459,17 +2483,16 @@ llvm::Constant *CGObjCMac::EmitMethodList(llvm::Twine Name, if (Methods.empty()) return llvm::Constant::getNullValue(ObjCTypes.MethodListPtrTy); - std::vector<llvm::Constant*> Values(3); + llvm::Constant *Values[3]; Values[0] = llvm::Constant::getNullValue(ObjCTypes.Int8PtrTy); Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Methods.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.MethodTy, Methods.size()); Values[2] = llvm::ConstantArray::get(AT, Methods); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV = CreateMetadataVar(Name, Init, Section, 4, true); - return llvm::ConstantExpr::getBitCast(GV, - ObjCTypes.MethodListPtrTy); + return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.MethodListPtrTy); } llvm::Function *CGObjCCommonMac::GenerateMethod(const ObjCMethodDecl *OMD, @@ -2558,7 +2581,7 @@ namespace { : S(*S), SyncArgSlot(SyncArgSlot), CallTryExitVar(CallTryExitVar), ExceptionData(ExceptionData), ObjCTypes(*ObjCTypes) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { // Check whether we need to call objc_exception_try_exit. // In optimized code, this branch will always be folded. llvm::BasicBlock *FinallyCallExit = @@ -2674,14 +2697,12 @@ FragileHazards::FragileHazards(CodeGenFunction &CGF) : CGF(CGF) { void FragileHazards::emitWriteHazard() { if (Locals.empty()) return; - CGF.Builder.CreateCall(WriteHazard, Locals.begin(), Locals.end()) - ->setDoesNotThrow(); + CGF.Builder.CreateCall(WriteHazard, Locals)->setDoesNotThrow(); } void FragileHazards::emitReadHazard(CGBuilderTy &Builder) { assert(!Locals.empty()); - Builder.CreateCall(ReadHazard, Locals.begin(), Locals.end()) - ->setDoesNotThrow(); + Builder.CreateCall(ReadHazard, Locals)->setDoesNotThrow(); } /// Emit read hazards in all the protected blocks, i.e. all the blocks @@ -2745,7 +2766,7 @@ void FragileHazards::collectLocals() { } llvm::FunctionType *FragileHazards::GetAsmFnType() { - llvm::SmallVector<const llvm::Type *, 16> tys(Locals.size()); + llvm::SmallVector<llvm::Type *, 16> tys(Locals.size()); for (unsigned i = 0, e = Locals.size(); i != e; ++i) tys[i] = Locals[i]->getType(); return llvm::FunctionType::get(CGF.VoidTy, tys, false); @@ -3389,7 +3410,7 @@ void CGObjCCommonMac::EmitImageInfo() { Section = "__DATA, __objc_imageinfo, regular, no_dead_strip"; llvm::GlobalVariable *GV = CreateMetadataVar("\01L_OBJC_IMAGE_INFO", - llvm::ConstantArray::get(AT, values, 2), + llvm::ConstantArray::get(AT, values), Section, 0, true); @@ -3430,7 +3451,7 @@ llvm::Constant *CGObjCMac::EmitModuleSymbols() { if (!NumClasses && !NumCategories) return llvm::Constant::getNullValue(ObjCTypes.SymtabPtrTy); - std::vector<llvm::Constant*> Values(5); + llvm::Constant *Values[5]; Values[0] = llvm::ConstantInt::get(ObjCTypes.LongTy, 0); Values[1] = llvm::Constant::getNullValue(ObjCTypes.SelectorPtrTy); Values[2] = llvm::ConstantInt::get(ObjCTypes.ShortTy, NumClasses); @@ -3452,7 +3473,7 @@ llvm::Constant *CGObjCMac::EmitModuleSymbols() { NumClasses + NumCategories), Symbols); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV = CreateMetadataVar("\01L_OBJC_SYMBOLS", Init, @@ -3461,25 +3482,35 @@ llvm::Constant *CGObjCMac::EmitModuleSymbols() { return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.SymtabPtrTy); } -llvm::Value *CGObjCMac::EmitClassRef(CGBuilderTy &Builder, - const ObjCInterfaceDecl *ID) { - LazySymbols.insert(ID->getIdentifier()); - - llvm::GlobalVariable *&Entry = ClassReferences[ID->getIdentifier()]; - +llvm::Value *CGObjCMac::EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II) { + LazySymbols.insert(II); + + llvm::GlobalVariable *&Entry = ClassReferences[II]; + if (!Entry) { llvm::Constant *Casted = - llvm::ConstantExpr::getBitCast(GetClassName(ID->getIdentifier()), - ObjCTypes.ClassPtrTy); + llvm::ConstantExpr::getBitCast(GetClassName(II), + ObjCTypes.ClassPtrTy); Entry = - CreateMetadataVar("\01L_OBJC_CLASS_REFERENCES_", Casted, - "__OBJC,__cls_refs,literal_pointers,no_dead_strip", - 4, true); + CreateMetadataVar("\01L_OBJC_CLASS_REFERENCES_", Casted, + "__OBJC,__cls_refs,literal_pointers,no_dead_strip", + 4, true); } - + return Builder.CreateLoad(Entry, "tmp"); } +llvm::Value *CGObjCMac::EmitClassRef(CGBuilderTy &Builder, + const ObjCInterfaceDecl *ID) { + return EmitClassRefFromId(Builder, ID->getIdentifier()); +} + +llvm::Value *CGObjCMac::EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder) { + IdentifierInfo *II = &CGM.getContext().Idents.get("NSAutoreleasePool"); + return EmitClassRefFromId(Builder, II); +} + llvm::Value *CGObjCMac::EmitSelector(CGBuilderTy &Builder, Selector Sel, bool lvalue) { llvm::GlobalVariable *&Entry = SelectorReferences[Sel]; @@ -3567,12 +3598,18 @@ void CGObjCCommonMac::BuildAggrIvarLayout(const ObjCImplementationDecl *OI, uint64_t MaxFieldOffset = 0; uint64_t MaxSkippedFieldOffset = 0; uint64_t LastBitfieldOrUnnamedOffset = 0; + uint64_t FirstFieldDelta = 0; if (RecFields.empty()) return; unsigned WordSizeInBits = CGM.getContext().Target.getPointerWidth(0); unsigned ByteSizeInBits = CGM.getContext().Target.getCharWidth(); - + if (!RD && CGM.getLangOptions().ObjCAutoRefCount) { + FieldDecl *FirstField = RecFields[0]; + FirstFieldDelta = + ComputeIvarBaseOffset(CGM, OI, cast<ObjCIvarDecl>(FirstField)); + } + for (unsigned i = 0, e = RecFields.size(); i != e; ++i) { FieldDecl *Field = RecFields[i]; uint64_t FieldOffset; @@ -3580,9 +3617,10 @@ void CGObjCCommonMac::BuildAggrIvarLayout(const ObjCImplementationDecl *OI, // Note that 'i' here is actually the field index inside RD of Field, // although this dependency is hidden. const ASTRecordLayout &RL = CGM.getContext().getASTRecordLayout(RD); - FieldOffset = RL.getFieldOffset(i) / ByteSizeInBits; + FieldOffset = (RL.getFieldOffset(i) / ByteSizeInBits) - FirstFieldDelta; } else - FieldOffset = ComputeIvarBaseOffset(CGM, OI, cast<ObjCIvarDecl>(Field)); + FieldOffset = + ComputeIvarBaseOffset(CGM, OI, cast<ObjCIvarDecl>(Field)) - FirstFieldDelta; // Skip over unnamed or bitfields if (!Field->getIdentifier() || Field->isBitField()) { @@ -3861,16 +3899,25 @@ llvm::Constant *CGObjCCommonMac::BuildIvarLayout( bool hasUnion = false; const llvm::Type *PtrTy = llvm::Type::getInt8PtrTy(VMContext); - if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC) + if (CGM.getLangOptions().getGCMode() == LangOptions::NonGC && + !CGM.getLangOptions().ObjCAutoRefCount) return llvm::Constant::getNullValue(PtrTy); - llvm::SmallVector<ObjCIvarDecl*, 32> Ivars; - const ObjCInterfaceDecl *OI = OMD->getClassInterface(); - CGM.getContext().DeepCollectObjCIvars(OI, true, Ivars); - + ObjCInterfaceDecl *OI = + const_cast<ObjCInterfaceDecl*>(OMD->getClassInterface()); llvm::SmallVector<FieldDecl*, 32> RecFields; - for (unsigned k = 0, e = Ivars.size(); k != e; ++k) - RecFields.push_back(cast<FieldDecl>(Ivars[k])); + if (CGM.getLangOptions().ObjCAutoRefCount) { + for (ObjCIvarDecl *IVD = OI->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) + RecFields.push_back(cast<FieldDecl>(IVD)); + } + else { + llvm::SmallVector<ObjCIvarDecl*, 32> Ivars; + CGM.getContext().DeepCollectObjCIvars(OI, true, Ivars); + + for (unsigned k = 0, e = Ivars.size(); k != e; ++k) + RecFields.push_back(cast<FieldDecl>(Ivars[k])); + } if (RecFields.empty()) return llvm::Constant::getNullValue(PtrTy); @@ -4113,21 +4160,19 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm) // char *name; // char *attributes; // } - PropertyTy = llvm::StructType::get(VMContext, Int8PtrTy, Int8PtrTy, NULL); - CGM.getModule().addTypeName("struct._prop_t", - PropertyTy); + PropertyTy = llvm::StructType::createNamed("struct._prop_t", + Int8PtrTy, Int8PtrTy, NULL); // struct _prop_list_t { // uint32_t entsize; // sizeof(struct _prop_t) // uint32_t count_of_properties; // struct _prop_t prop_list[count_of_properties]; // } - PropertyListTy = llvm::StructType::get(VMContext, IntTy, - IntTy, - llvm::ArrayType::get(PropertyTy, 0), - NULL); - CGM.getModule().addTypeName("struct._prop_list_t", - PropertyListTy); + PropertyListTy = + llvm::StructType::createNamed("struct._prop_list_t", + IntTy, IntTy, + llvm::ArrayType::get(PropertyTy, 0), + NULL); // struct _prop_list_t * PropertyListPtrTy = llvm::PointerType::getUnqual(PropertyListTy); @@ -4136,16 +4181,14 @@ ObjCCommonTypesHelper::ObjCCommonTypesHelper(CodeGen::CodeGenModule &cgm) // char *method_type; // char *_imp; // } - MethodTy = llvm::StructType::get(VMContext, SelectorPtrTy, - Int8PtrTy, - Int8PtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_method", MethodTy); + MethodTy = llvm::StructType::createNamed("struct._objc_method", + SelectorPtrTy, Int8PtrTy, Int8PtrTy, + NULL); // struct _objc_cache * - CacheTy = llvm::OpaqueType::get(VMContext); - CGM.getModule().addTypeName("struct._objc_cache", CacheTy); + CacheTy = llvm::StructType::createNamed(VMContext, "struct._objc_cache"); CachePtrTy = llvm::PointerType::getUnqual(CacheTy); + } ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) @@ -4155,22 +4198,18 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // char *types; // } MethodDescriptionTy = - llvm::StructType::get(VMContext, SelectorPtrTy, - Int8PtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_method_description", - MethodDescriptionTy); + llvm::StructType::createNamed("struct._objc_method_description", + SelectorPtrTy, Int8PtrTy, NULL); // struct _objc_method_description_list { // int count; // struct _objc_method_description[1]; // } MethodDescriptionListTy = - llvm::StructType::get(VMContext, IntTy, - llvm::ArrayType::get(MethodDescriptionTy, 0), - NULL); - CGM.getModule().addTypeName("struct._objc_method_description_list", - MethodDescriptionListTy); + llvm::StructType::createNamed("struct._objc_method_description_list", + IntTy, + llvm::ArrayType::get(MethodDescriptionTy, 0), + NULL); // struct _objc_method_description_list * MethodDescriptionListPtrTy = @@ -4185,29 +4224,27 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // struct _objc_property_list *instance_properties; // } ProtocolExtensionTy = - llvm::StructType::get(VMContext, IntTy, - MethodDescriptionListPtrTy, - MethodDescriptionListPtrTy, - PropertyListPtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_protocol_extension", - ProtocolExtensionTy); + llvm::StructType::createNamed("struct._objc_protocol_extension", + IntTy, + MethodDescriptionListPtrTy, + MethodDescriptionListPtrTy, + PropertyListPtrTy, + NULL); // struct _objc_protocol_extension * ProtocolExtensionPtrTy = llvm::PointerType::getUnqual(ProtocolExtensionTy); // Handle recursive construction of Protocol and ProtocolList types - llvm::PATypeHolder ProtocolTyHolder = llvm::OpaqueType::get(VMContext); - llvm::PATypeHolder ProtocolListTyHolder = llvm::OpaqueType::get(VMContext); + ProtocolTy = + llvm::StructType::createNamed(VMContext, "struct._objc_protocol"); - const llvm::Type *T = - llvm::StructType::get(VMContext, - llvm::PointerType::getUnqual(ProtocolListTyHolder), + ProtocolListTy = + llvm::StructType::createNamed(VMContext, "struct._objc_protocol_list"); + ProtocolListTy->setBody(llvm::PointerType::getUnqual(ProtocolListTy), LongTy, - llvm::ArrayType::get(ProtocolTyHolder, 0), + llvm::ArrayType::get(ProtocolTy, 0), NULL); - cast<llvm::OpaqueType>(ProtocolListTyHolder.get())->refineAbstractTypeTo(T); // struct _objc_protocol { // struct _objc_protocol_extension *isa; @@ -4216,22 +4253,15 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // struct _objc_method_description_list *instance_methods; // struct _objc_method_description_list *class_methods; // } - T = llvm::StructType::get(VMContext, ProtocolExtensionPtrTy, - Int8PtrTy, - llvm::PointerType::getUnqual(ProtocolListTyHolder), - MethodDescriptionListPtrTy, - MethodDescriptionListPtrTy, - NULL); - cast<llvm::OpaqueType>(ProtocolTyHolder.get())->refineAbstractTypeTo(T); - - ProtocolListTy = cast<llvm::StructType>(ProtocolListTyHolder.get()); - CGM.getModule().addTypeName("struct._objc_protocol_list", - ProtocolListTy); + ProtocolTy->setBody(ProtocolExtensionPtrTy, Int8PtrTy, + llvm::PointerType::getUnqual(ProtocolListTy), + MethodDescriptionListPtrTy, + MethodDescriptionListPtrTy, + NULL); + // struct _objc_protocol_list * ProtocolListPtrTy = llvm::PointerType::getUnqual(ProtocolListTy); - ProtocolTy = cast<llvm::StructType>(ProtocolTyHolder.get()); - CGM.getModule().addTypeName("struct._objc_protocol", ProtocolTy); ProtocolPtrTy = llvm::PointerType::getUnqual(ProtocolTy); // Class description structures @@ -4241,32 +4271,26 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // char *ivar_type; // int ivar_offset; // } - IvarTy = llvm::StructType::get(VMContext, Int8PtrTy, - Int8PtrTy, - IntTy, - NULL); - CGM.getModule().addTypeName("struct._objc_ivar", IvarTy); + IvarTy = llvm::StructType::createNamed("struct._objc_ivar", + Int8PtrTy, Int8PtrTy, IntTy, NULL); // struct _objc_ivar_list * - IvarListTy = llvm::OpaqueType::get(VMContext); - CGM.getModule().addTypeName("struct._objc_ivar_list", IvarListTy); + IvarListTy = + llvm::StructType::createNamed(VMContext, "struct._objc_ivar_list"); IvarListPtrTy = llvm::PointerType::getUnqual(IvarListTy); // struct _objc_method_list * - MethodListTy = llvm::OpaqueType::get(VMContext); - CGM.getModule().addTypeName("struct._objc_method_list", MethodListTy); + MethodListTy = + llvm::StructType::createNamed(VMContext, "struct._objc_method_list"); MethodListPtrTy = llvm::PointerType::getUnqual(MethodListTy); // struct _objc_class_extension * ClassExtensionTy = - llvm::StructType::get(VMContext, IntTy, - Int8PtrTy, - PropertyListPtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_class_extension", ClassExtensionTy); + llvm::StructType::createNamed("struct._objc_class_extension", + IntTy, Int8PtrTy, PropertyListPtrTy, NULL); ClassExtensionPtrTy = llvm::PointerType::getUnqual(ClassExtensionTy); - llvm::PATypeHolder ClassTyHolder = llvm::OpaqueType::get(VMContext); + ClassTy = llvm::StructType::createNamed(VMContext, "struct._objc_class"); // struct _objc_class { // Class isa; @@ -4282,24 +4306,20 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // char *ivar_layout; // struct _objc_class_ext *ext; // }; - T = llvm::StructType::get(VMContext, - llvm::PointerType::getUnqual(ClassTyHolder), - llvm::PointerType::getUnqual(ClassTyHolder), - Int8PtrTy, - LongTy, - LongTy, - LongTy, - IvarListPtrTy, - MethodListPtrTy, - CachePtrTy, - ProtocolListPtrTy, - Int8PtrTy, - ClassExtensionPtrTy, - NULL); - cast<llvm::OpaqueType>(ClassTyHolder.get())->refineAbstractTypeTo(T); - - ClassTy = cast<llvm::StructType>(ClassTyHolder.get()); - CGM.getModule().addTypeName("struct._objc_class", ClassTy); + ClassTy->setBody(llvm::PointerType::getUnqual(ClassTy), + llvm::PointerType::getUnqual(ClassTy), + Int8PtrTy, + LongTy, + LongTy, + LongTy, + IvarListPtrTy, + MethodListPtrTy, + CachePtrTy, + ProtocolListPtrTy, + Int8PtrTy, + ClassExtensionPtrTy, + NULL); + ClassPtrTy = llvm::PointerType::getUnqual(ClassTy); // struct _objc_category { @@ -4310,15 +4330,11 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // uint32_t size; // sizeof(struct _objc_category) // struct _objc_property_list *instance_properties;// category's @property // } - CategoryTy = llvm::StructType::get(VMContext, Int8PtrTy, - Int8PtrTy, - MethodListPtrTy, - MethodListPtrTy, - ProtocolListPtrTy, - IntTy, - PropertyListPtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_category", CategoryTy); + CategoryTy = + llvm::StructType::createNamed("struct._objc_category", + Int8PtrTy, Int8PtrTy, MethodListPtrTy, + MethodListPtrTy, ProtocolListPtrTy, + IntTy, PropertyListPtrTy, NULL); // Global metadata structures @@ -4329,13 +4345,10 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // short cat_def_cnt; // char *defs[cls_def_cnt + cat_def_cnt]; // } - SymtabTy = llvm::StructType::get(VMContext, LongTy, - SelectorPtrTy, - ShortTy, - ShortTy, - llvm::ArrayType::get(Int8PtrTy, 0), - NULL); - CGM.getModule().addTypeName("struct._objc_symtab", SymtabTy); + SymtabTy = + llvm::StructType::createNamed("struct._objc_symtab", + LongTy, SelectorPtrTy, ShortTy, ShortTy, + llvm::ArrayType::get(Int8PtrTy, 0), NULL); SymtabPtrTy = llvm::PointerType::getUnqual(SymtabTy); // struct _objc_module { @@ -4345,12 +4358,8 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) // struct _objc_symtab* symtab; // } ModuleTy = - llvm::StructType::get(VMContext, LongTy, - LongTy, - Int8PtrTy, - SymtabPtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_module", ModuleTy); + llvm::StructType::createNamed("struct._objc_module", + LongTy, LongTy, Int8PtrTy, SymtabPtrTy, NULL); // FIXME: This is the size of the setjmp buffer and should be target @@ -4358,16 +4367,14 @@ ObjCTypesHelper::ObjCTypesHelper(CodeGen::CodeGenModule &cgm) uint64_t SetJmpBufferSize = 18; // Exceptions - const llvm::Type *StackPtrTy = llvm::ArrayType::get( + llvm::Type *StackPtrTy = llvm::ArrayType::get( llvm::Type::getInt8PtrTy(VMContext), 4); ExceptionDataTy = - llvm::StructType::get(VMContext, - llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), - SetJmpBufferSize), - StackPtrTy, NULL); - CGM.getModule().addTypeName("struct._objc_exception_data", - ExceptionDataTy); + llvm::StructType::createNamed("struct._objc_exception_data", + llvm::ArrayType::get(llvm::Type::getInt32Ty(VMContext), + SetJmpBufferSize), + StackPtrTy, NULL); } @@ -4378,12 +4385,11 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // uint32_t method_count; // struct _objc_method method_list[method_count]; // } - MethodListnfABITy = llvm::StructType::get(VMContext, IntTy, - IntTy, - llvm::ArrayType::get(MethodTy, 0), - NULL); - CGM.getModule().addTypeName("struct.__method_list_t", - MethodListnfABITy); + MethodListnfABITy = + llvm::StructType::createNamed("struct.__method_list_t", + IntTy, IntTy, + llvm::ArrayType::get(MethodTy, 0), + NULL); // struct method_list_t * MethodListnfABIPtrTy = llvm::PointerType::getUnqual(MethodListnfABITy); @@ -4401,22 +4407,21 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // } // Holder for struct _protocol_list_t * - llvm::PATypeHolder ProtocolListTyHolder = llvm::OpaqueType::get(VMContext); - - ProtocolnfABITy = llvm::StructType::get(VMContext, ObjectPtrTy, - Int8PtrTy, - llvm::PointerType::getUnqual( - ProtocolListTyHolder), - MethodListnfABIPtrTy, - MethodListnfABIPtrTy, - MethodListnfABIPtrTy, - MethodListnfABIPtrTy, - PropertyListPtrTy, - IntTy, - IntTy, - NULL); - CGM.getModule().addTypeName("struct._protocol_t", - ProtocolnfABITy); + ProtocolListnfABITy = + llvm::StructType::createNamed(VMContext, "struct._objc_protocol_list"); + + ProtocolnfABITy = + llvm::StructType::createNamed("struct._protocol_t", + ObjectPtrTy, Int8PtrTy, + llvm::PointerType::getUnqual(ProtocolListnfABITy), + MethodListnfABIPtrTy, + MethodListnfABIPtrTy, + MethodListnfABIPtrTy, + MethodListnfABIPtrTy, + PropertyListPtrTy, + IntTy, + IntTy, + NULL); // struct _protocol_t* ProtocolnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolnfABITy); @@ -4425,14 +4430,9 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // long protocol_count; // Note, this is 32/64 bit // struct _protocol_t *[protocol_count]; // } - ProtocolListnfABITy = llvm::StructType::get(VMContext, LongTy, - llvm::ArrayType::get( - ProtocolnfABIPtrTy, 0), - NULL); - CGM.getModule().addTypeName("struct._objc_protocol_list", - ProtocolListnfABITy); - cast<llvm::OpaqueType>(ProtocolListTyHolder.get())->refineAbstractTypeTo( - ProtocolListnfABITy); + ProtocolListnfABITy->setBody(LongTy, + llvm::ArrayType::get(ProtocolnfABIPtrTy, 0), + NULL); // struct _objc_protocol_list* ProtocolListnfABIPtrTy = llvm::PointerType::getUnqual(ProtocolListnfABITy); @@ -4444,26 +4444,25 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // uint32_t alignment; // uint32_t size; // } - IvarnfABITy = llvm::StructType::get(VMContext, - llvm::PointerType::getUnqual(LongTy), - Int8PtrTy, - Int8PtrTy, - IntTy, - IntTy, - NULL); - CGM.getModule().addTypeName("struct._ivar_t", IvarnfABITy); + IvarnfABITy = + llvm::StructType::createNamed("struct._ivar_t", + llvm::PointerType::getUnqual(LongTy), + Int8PtrTy, + Int8PtrTy, + IntTy, + IntTy, + NULL); // struct _ivar_list_t { // uint32 entsize; // sizeof(struct _ivar_t) // uint32 count; // struct _iver_t list[count]; // } - IvarListnfABITy = llvm::StructType::get(VMContext, IntTy, - IntTy, - llvm::ArrayType::get( - IvarnfABITy, 0), - NULL); - CGM.getModule().addTypeName("struct._ivar_list_t", IvarListnfABITy); + IvarListnfABITy = + llvm::StructType::createNamed("struct._ivar_list_t", + IntTy, IntTy, + llvm::ArrayType::get(IvarnfABITy, 0), + NULL); IvarListnfABIPtrTy = llvm::PointerType::getUnqual(IvarListnfABITy); @@ -4482,22 +4481,21 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // } // FIXME. Add 'reserved' field in 64bit abi mode! - ClassRonfABITy = llvm::StructType::get(VMContext, IntTy, - IntTy, - IntTy, - Int8PtrTy, - Int8PtrTy, - MethodListnfABIPtrTy, - ProtocolListnfABIPtrTy, - IvarListnfABIPtrTy, - Int8PtrTy, - PropertyListPtrTy, - NULL); - CGM.getModule().addTypeName("struct._class_ro_t", - ClassRonfABITy); + ClassRonfABITy = llvm::StructType::createNamed("struct._class_ro_t", + IntTy, + IntTy, + IntTy, + Int8PtrTy, + Int8PtrTy, + MethodListnfABIPtrTy, + ProtocolListnfABIPtrTy, + IvarListnfABIPtrTy, + Int8PtrTy, + PropertyListPtrTy, + NULL); // ImpnfABITy - LLVM for id (*)(id, SEL, ...) - const llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; + llvm::Type *params[] = { ObjectPtrTy, SelectorPtrTy }; ImpnfABITy = llvm::FunctionType::get(ObjectPtrTy, params, false) ->getPointerTo(); @@ -4509,19 +4507,13 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // struct class_ro_t *ro; // } - llvm::PATypeHolder ClassTyHolder = llvm::OpaqueType::get(VMContext); - ClassnfABITy = - llvm::StructType::get(VMContext, - llvm::PointerType::getUnqual(ClassTyHolder), - llvm::PointerType::getUnqual(ClassTyHolder), - CachePtrTy, - llvm::PointerType::getUnqual(ImpnfABITy), - llvm::PointerType::getUnqual(ClassRonfABITy), - NULL); - CGM.getModule().addTypeName("struct._class_t", ClassnfABITy); - - cast<llvm::OpaqueType>(ClassTyHolder.get())->refineAbstractTypeTo( - ClassnfABITy); + ClassnfABITy = llvm::StructType::createNamed(VMContext, "struct._class_t"); + ClassnfABITy->setBody(llvm::PointerType::getUnqual(ClassnfABITy), + llvm::PointerType::getUnqual(ClassnfABITy), + CachePtrTy, + llvm::PointerType::getUnqual(ImpnfABITy), + llvm::PointerType::getUnqual(ClassRonfABITy), + NULL); // LLVM for struct _class_t * ClassnfABIPtrTy = llvm::PointerType::getUnqual(ClassnfABITy); @@ -4534,14 +4526,14 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // const struct _protocol_list_t * const protocols; // const struct _prop_list_t * const properties; // } - CategorynfABITy = llvm::StructType::get(VMContext, Int8PtrTy, - ClassnfABIPtrTy, - MethodListnfABIPtrTy, - MethodListnfABIPtrTy, - ProtocolListnfABIPtrTy, - PropertyListPtrTy, - NULL); - CGM.getModule().addTypeName("struct._category_t", CategorynfABITy); + CategorynfABITy = llvm::StructType::createNamed("struct._category_t", + Int8PtrTy, + ClassnfABIPtrTy, + MethodListnfABIPtrTy, + MethodListnfABIPtrTy, + ProtocolListnfABIPtrTy, + PropertyListPtrTy, + NULL); // New types for nonfragile abi messaging. CodeGen::CodeGenTypes &Types = CGM.getTypes(); @@ -4576,26 +4568,25 @@ ObjCNonFragileABITypesHelper::ObjCNonFragileABITypesHelper(CodeGen::CodeGenModul // SUPER_IMP messenger; // SEL name; // }; - SuperMessageRefTy = llvm::StructType::get(VMContext, ImpnfABITy, - SelectorPtrTy, - NULL); - CGM.getModule().addTypeName("struct._super_message_ref_t", SuperMessageRefTy); + SuperMessageRefTy = + llvm::StructType::createNamed("struct._super_message_ref_t", + ImpnfABITy, SelectorPtrTy, NULL); // SuperMessageRefPtrTy - LLVM for struct _super_message_ref_t* SuperMessageRefPtrTy = llvm::PointerType::getUnqual(SuperMessageRefTy); - + // struct objc_typeinfo { // const void** vtable; // objc_ehtype_vtable + 2 // const char* name; // c++ typeinfo string // Class cls; // }; - EHTypeTy = llvm::StructType::get(VMContext, - llvm::PointerType::getUnqual(Int8PtrTy), - Int8PtrTy, - ClassnfABIPtrTy, - NULL); - CGM.getModule().addTypeName("struct._objc_typeinfo", EHTypeTy); + EHTypeTy = + llvm::StructType::createNamed("struct._objc_typeinfo", + llvm::PointerType::getUnqual(Int8PtrTy), + Int8PtrTy, + ClassnfABIPtrTy, + NULL); EHTypePtrTy = llvm::PointerType::getUnqual(EHTypeTy); } @@ -4743,7 +4734,12 @@ enum MetaDataDlags { CLS_META = 0x1, CLS_ROOT = 0x2, OBJC2_CLS_HIDDEN = 0x10, - CLS_EXCEPTION = 0x20 + CLS_EXCEPTION = 0x20, + + /// (Obsolete) ARC-specific: this class has a .release_ivars method + CLS_HAS_IVAR_RELEASER = 0x40, + /// class was compiled with -fobjc-arr + CLS_COMPILED_BY_ARC = 0x80 // (1<<7) }; /// BuildClassRoTInitializer - generate meta-data for: /// struct _class_ro_t { @@ -4767,6 +4763,10 @@ llvm::GlobalVariable * CGObjCNonFragileABIMac::BuildClassRoTInitializer( const ObjCImplementationDecl *ID) { std::string ClassName = ID->getNameAsString(); std::vector<llvm::Constant*> Values(10); // 11 for 64bit targets! + + if (CGM.getLangOptions().ObjCAutoRefCount) + flags |= CLS_COMPILED_BY_ARC; + Values[ 0] = llvm::ConstantInt::get(ObjCTypes.IntTy, flags); Values[ 1] = llvm::ConstantInt::get(ObjCTypes.IntTy, InstanceStart); Values[ 2] = llvm::ConstantInt::get(ObjCTypes.IntTy, InstanceSize); @@ -4936,7 +4936,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { ID->getClassInterface()->getVisibility() == HiddenVisibility; if (classIsHidden) flags |= OBJC2_CLS_HIDDEN; - if (ID->getNumIvarInitializers()) + if (ID->hasCXXStructors()) flags |= eClassFlags_ABI2_HasCXXStructors; if (!ID->getClassInterface()->getSuperClass()) { // class is root @@ -4972,7 +4972,7 @@ void CGObjCNonFragileABIMac::GenerateClass(const ObjCImplementationDecl *ID) { flags = CLS; if (classIsHidden) flags |= OBJC2_CLS_HIDDEN; - if (ID->getNumIvarInitializers()) + if (ID->hasCXXStructors()) flags |= eClassFlags_ABI2_HasCXXStructors; if (hasObjCExceptionAttribute(CGM.getContext(), ID->getClassInterface())) @@ -5174,7 +5174,7 @@ llvm::Constant *CGObjCNonFragileABIMac::EmitMethodList(llvm::Twine Name, if (Methods.empty()) return llvm::Constant::getNullValue(ObjCTypes.MethodListnfABIPtrTy); - std::vector<llvm::Constant*> Values(3); + llvm::Constant *Values[3]; // sizeof(struct _objc_method) unsigned Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.MethodTy); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); @@ -5183,19 +5183,15 @@ llvm::Constant *CGObjCNonFragileABIMac::EmitMethodList(llvm::Twine Name, llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.MethodTy, Methods.size()); Values[2] = llvm::ConstantArray::get(AT, Methods); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, - llvm::GlobalValue::InternalLinkage, - Init, - Name); - GV->setAlignment( - CGM.getTargetData().getABITypeAlignment(Init->getType())); + llvm::GlobalValue::InternalLinkage, Init, Name); + GV->setAlignment(CGM.getTargetData().getABITypeAlignment(Init->getType())); GV->setSection(Section); CGM.AddUsedGlobal(GV); - return llvm::ConstantExpr::getBitCast(GV, - ObjCTypes.MethodListnfABIPtrTy); + return llvm::ConstantExpr::getBitCast(GV, ObjCTypes.MethodListnfABIPtrTy); } /// ObjCIvarOffsetVariable - Returns the ivar offset variable for @@ -5262,17 +5258,14 @@ llvm::Constant *CGObjCNonFragileABIMac::EmitIvarList( std::vector<llvm::Constant*> Ivars, Ivar(5); - const ObjCInterfaceDecl *OID = ID->getClassInterface(); + ObjCInterfaceDecl *OID = + const_cast<ObjCInterfaceDecl*>(ID->getClassInterface()); assert(OID && "CGObjCNonFragileABIMac::EmitIvarList - null interface"); // FIXME. Consolidate this with similar code in GenerateClass. - // Collect declared and synthesized ivars in a small vector. - llvm::SmallVector<ObjCIvarDecl*, 16> OIvars; - CGM.getContext().ShallowCollectObjCIvars(OID, OIvars); - - for (unsigned i = 0, e = OIvars.size(); i != e; ++i) { - ObjCIvarDecl *IVD = OIvars[i]; + for (ObjCIvarDecl *IVD = OID->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { // Ignore unnamed bit-fields. if (!IVD->getDeclName()) continue; @@ -5298,14 +5291,15 @@ llvm::Constant *CGObjCNonFragileABIMac::EmitIvarList( // Return null for empty list. if (Ivars.empty()) return llvm::Constant::getNullValue(ObjCTypes.IvarListnfABIPtrTy); - std::vector<llvm::Constant*> Values(3); + + llvm::Constant *Values[3]; unsigned Size = CGM.getTargetData().getTypeAllocSize(ObjCTypes.IvarnfABITy); Values[0] = llvm::ConstantInt::get(ObjCTypes.IntTy, Size); Values[1] = llvm::ConstantInt::get(ObjCTypes.IntTy, Ivars.size()); llvm::ArrayType *AT = llvm::ArrayType::get(ObjCTypes.IvarnfABITy, Ivars.size()); Values[2] = llvm::ConstantArray::get(AT, Ivars); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); const char *Prefix = "\01l_OBJC_$_INSTANCE_VARIABLES_"; llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, @@ -5491,7 +5485,7 @@ CGObjCNonFragileABIMac::EmitProtocolList(llvm::Twine Name, ProtocolRefs.push_back(llvm::Constant::getNullValue( ObjCTypes.ProtocolnfABIPtrTy)); - std::vector<llvm::Constant*> Values(2); + llvm::Constant *Values[2]; Values[0] = llvm::ConstantInt::get(ObjCTypes.LongTy, ProtocolRefs.size() - 1); Values[1] = @@ -5500,11 +5494,10 @@ CGObjCNonFragileABIMac::EmitProtocolList(llvm::Twine Name, ProtocolRefs.size()), ProtocolRefs); - llvm::Constant *Init = llvm::ConstantStruct::get(VMContext, Values, false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Values); GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), false, llvm::GlobalValue::InternalLinkage, - Init, - Name); + Init, Name); GV->setSection("__DATA, __objc_const"); GV->setAlignment( CGM.getTargetData().getABITypeAlignment(Init->getType())); @@ -5654,8 +5647,7 @@ CGObjCNonFragileABIMac::EmitVTableMessageSend(CodeGenFunction &CGF, if (!messageRef) { // Build the message ref structure. llvm::Constant *values[] = { fn, GetMethodVarName(selector) }; - llvm::Constant *init = - llvm::ConstantStruct::get(VMContext, values, 2, false); + llvm::Constant *init = llvm::ConstantStruct::getAnon(values); messageRef = new llvm::GlobalVariable(CGM.getModule(), init->getType(), /*constant*/ false, @@ -5719,28 +5711,39 @@ CGObjCNonFragileABIMac::GetClassGlobal(const std::string &Name) { return GV; } -llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CGBuilderTy &Builder, - const ObjCInterfaceDecl *ID) { - llvm::GlobalVariable *&Entry = ClassReferences[ID->getIdentifier()]; - +llvm::Value *CGObjCNonFragileABIMac::EmitClassRefFromId(CGBuilderTy &Builder, + IdentifierInfo *II) { + llvm::GlobalVariable *&Entry = ClassReferences[II]; + if (!Entry) { - std::string ClassName(getClassSymbolPrefix() + ID->getNameAsString()); + std::string ClassName(getClassSymbolPrefix() + II->getName().str()); llvm::GlobalVariable *ClassGV = GetClassGlobal(ClassName); Entry = - new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, - false, llvm::GlobalValue::InternalLinkage, - ClassGV, - "\01L_OBJC_CLASSLIST_REFERENCES_$_"); + new llvm::GlobalVariable(CGM.getModule(), ObjCTypes.ClassnfABIPtrTy, + false, llvm::GlobalValue::InternalLinkage, + ClassGV, + "\01L_OBJC_CLASSLIST_REFERENCES_$_"); Entry->setAlignment( - CGM.getTargetData().getABITypeAlignment( - ObjCTypes.ClassnfABIPtrTy)); + CGM.getTargetData().getABITypeAlignment( + ObjCTypes.ClassnfABIPtrTy)); Entry->setSection("__DATA, __objc_classrefs, regular, no_dead_strip"); CGM.AddUsedGlobal(Entry); } - + return Builder.CreateLoad(Entry, "tmp"); } +llvm::Value *CGObjCNonFragileABIMac::EmitClassRef(CGBuilderTy &Builder, + const ObjCInterfaceDecl *ID) { + return EmitClassRefFromId(Builder, ID->getIdentifier()); +} + +llvm::Value *CGObjCNonFragileABIMac::EmitNSAutoreleasePoolClassRef( + CGBuilderTy &Builder) { + IdentifierInfo *II = &CGM.getContext().Idents.get("NSAutoreleasePool"); + return EmitClassRefFromId(Builder, II); +} + llvm::Value * CGObjCNonFragileABIMac::EmitSuperClassRef(CGBuilderTy &Builder, const ObjCInterfaceDecl *ID) { @@ -6043,12 +6046,10 @@ void CGObjCNonFragileABIMac::EmitThrowStmt(CodeGen::CodeGenFunction &CGF, llvm::Value *Exception = CGF.EmitScalarExpr(ThrowExpr); Exception = CGF.Builder.CreateBitCast(Exception, ObjCTypes.ObjectPtrTy, "tmp"); - llvm::Value *Args[] = { Exception }; - CGF.EmitCallOrInvoke(ObjCTypes.getExceptionThrowFn(), - Args, Args+1) + CGF.EmitCallOrInvoke(ObjCTypes.getExceptionThrowFn(), Exception) .setDoesNotReturn(); } else { - CGF.EmitCallOrInvoke(ObjCTypes.getExceptionRethrowFn(), 0, 0) + CGF.EmitCallOrInvoke(ObjCTypes.getExceptionRethrowFn()) .setDoesNotReturn(); } diff --git a/lib/CodeGen/CGObjCRuntime.cpp b/lib/CodeGen/CGObjCRuntime.cpp index 21150f1f848c..09c8d0b28f9c 100644 --- a/lib/CodeGen/CGObjCRuntime.cpp +++ b/lib/CodeGen/CGObjCRuntime.cpp @@ -52,14 +52,14 @@ static uint64_t LookupFieldBitOffset(CodeGen::CodeGenModule &CGM, // implemented. This should be fixed to get the information from the layout // directly. unsigned Index = 0; - llvm::SmallVector<ObjCIvarDecl*, 16> Ivars; - CGM.getContext().ShallowCollectObjCIvars(Container, Ivars); - for (unsigned k = 0, e = Ivars.size(); k != e; ++k) { - if (Ivar == Ivars[k]) + ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl*>(Container); + + for (ObjCIvarDecl *IVD = IDecl->all_declared_ivar_begin(); + IVD; IVD = IVD->getNextIvar()) { + if (Ivar == IVD) break; ++Index; } - assert(Index != Ivars.size() && "Ivar is not inside container!"); assert(Index < RL->getFieldCount() && "Ivar is not inside record layout!"); return RL->getFieldOffset(Index); @@ -134,7 +134,7 @@ LValue CGObjCRuntime::EmitValueForIvarAtOffset(CodeGen::CodeGenFunction &CGF, ContainingTypeSize, ContainingTypeAlign)); return LValue::MakeBitfield(V, *Info, - IvarTy.getCVRQualifiers() | CVRQualifiers); + IvarTy.withCVRQualifiers(CVRQualifiers)); } namespace { @@ -151,13 +151,13 @@ namespace { bool MightThrow; llvm::Value *Fn; - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { if (!MightThrow) { CGF.Builder.CreateCall(Fn)->setDoesNotThrow(); return; } - CGF.EmitCallOrInvoke(Fn, 0, 0); + CGF.EmitCallOrInvoke(Fn); } }; } @@ -175,10 +175,8 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, CodeGenFunction::FinallyInfo FinallyInfo; if (const ObjCAtFinallyStmt *Finally = S.getFinallyStmt()) - FinallyInfo = CGF.EnterFinallyBlock(Finally->getFinallyBody(), - beginCatchFn, - endCatchFn, - exceptionRethrowFn); + FinallyInfo.enter(CGF, Finally->getFinallyBody(), + beginCatchFn, endCatchFn, exceptionRethrowFn); llvm::SmallVector<CatchHandler, 8> Handlers; @@ -266,9 +264,9 @@ void CGObjCRuntime::EmitTryCatchStmt(CodeGenFunction &CGF, // Go back to the try-statement fallthrough. CGF.Builder.restoreIP(SavedIP); - // Pop out of the normal cleanup on the finally. + // Pop out of the finally. if (S.getFinallyStmt()) - CGF.ExitFinallyBlock(FinallyInfo); + FinallyInfo.exit(CGF); if (Cont.isValid()) CGF.EmitBlock(Cont.getBlock()); @@ -281,7 +279,7 @@ namespace { CallSyncExit(llvm::Value *SyncExitFn, llvm::Value *SyncArg) : SyncExitFn(SyncExitFn), SyncArg(SyncArg) {} - void Emit(CodeGenFunction &CGF, bool IsForEHCleanup) { + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.Builder.CreateCall(SyncExitFn, SyncArg)->setDoesNotThrow(); } }; diff --git a/lib/CodeGen/CGObjCRuntime.h b/lib/CodeGen/CGObjCRuntime.h index 866d5d83fe60..7accc70c9623 100644 --- a/lib/CodeGen/CGObjCRuntime.h +++ b/lib/CodeGen/CGObjCRuntime.h @@ -205,7 +205,13 @@ public: /// interface decl. virtual llvm::Value *GetClass(CGBuilderTy &Builder, const ObjCInterfaceDecl *OID) = 0; - + + + virtual llvm::Value *EmitNSAutoreleasePoolClassRef(CGBuilderTy &Builder) { + assert(false &&"autoreleasepool unsupported in this ABI"); + return 0; + } + /// EnumerationMutationFunction - Return the function that's called by the /// compiler when a mutation is detected during foreach iteration. virtual llvm::Constant *EnumerationMutationFunction() = 0; diff --git a/lib/CodeGen/CGRTTI.cpp b/lib/CodeGen/CGRTTI.cpp index c73b199e31f7..e564c7070525 100644 --- a/lib/CodeGen/CGRTTI.cpp +++ b/lib/CodeGen/CGRTTI.cpp @@ -658,9 +658,7 @@ llvm::Constant *RTTIBuilder::BuildTypeInfo(QualType Ty, bool Force) { break; } - llvm::Constant *Init = - llvm::ConstantStruct::get(VMContext, &Fields[0], Fields.size(), - /*Packed=*/false); + llvm::Constant *Init = llvm::ConstantStruct::getAnon(Fields); llvm::GlobalVariable *GV = new llvm::GlobalVariable(CGM.getModule(), Init->getType(), diff --git a/lib/CodeGen/CGRecordLayout.h b/lib/CodeGen/CGRecordLayout.h index 6d9fc0589e93..8a450298f70a 100644 --- a/lib/CodeGen/CGRecordLayout.h +++ b/lib/CodeGen/CGRecordLayout.h @@ -176,11 +176,11 @@ class CGRecordLayout { private: /// The LLVM type corresponding to this record layout; used when /// laying it out as a complete object. - llvm::PATypeHolder CompleteObjectType; + llvm::StructType *CompleteObjectType; /// The LLVM type for the non-virtual part of this record layout; /// used when laying it out as a base subobject. - llvm::PATypeHolder BaseSubobjectType; + llvm::StructType *BaseSubobjectType; /// Map from (non-bit-field) struct field to the corresponding llvm struct /// type field no. This info is populated by record builder. @@ -208,8 +208,8 @@ private: bool IsZeroInitializableAsBase : 1; public: - CGRecordLayout(const llvm::StructType *CompleteObjectType, - const llvm::StructType *BaseSubobjectType, + CGRecordLayout(llvm::StructType *CompleteObjectType, + llvm::StructType *BaseSubobjectType, bool IsZeroInitializable, bool IsZeroInitializableAsBase) : CompleteObjectType(CompleteObjectType), @@ -219,14 +219,14 @@ public: /// \brief Return the "complete object" LLVM type associated with /// this record. - const llvm::StructType *getLLVMType() const { - return cast<llvm::StructType>(CompleteObjectType.get()); + llvm::StructType *getLLVMType() const { + return CompleteObjectType; } /// \brief Return the "base subobject" LLVM type associated with /// this record. - const llvm::StructType *getBaseSubobjectLLVMType() const { - return cast<llvm::StructType>(BaseSubobjectType.get()); + llvm::StructType *getBaseSubobjectLLVMType() const { + return BaseSubobjectType; } /// \brief Check whether this struct can be C++ zero-initialized diff --git a/lib/CodeGen/CGRecordLayoutBuilder.cpp b/lib/CodeGen/CGRecordLayoutBuilder.cpp index 0d72f854ba3e..2b07bafa0096 100644 --- a/lib/CodeGen/CGRecordLayoutBuilder.cpp +++ b/lib/CodeGen/CGRecordLayoutBuilder.cpp @@ -18,6 +18,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/Expr.h" #include "clang/AST/RecordLayout.h" +#include "clang/Frontend/CodeGenOptions.h" #include "CodeGenTypes.h" #include "CGCXXABI.h" #include "llvm/DerivedTypes.h" @@ -34,7 +35,7 @@ class CGRecordLayoutBuilder { public: /// FieldTypes - Holds the LLVM types that the struct is created from. /// - llvm::SmallVector<const llvm::Type *, 16> FieldTypes; + llvm::SmallVector<llvm::Type *, 16> FieldTypes; /// BaseSubobjectType - Holds the LLVM type for the non-virtual part /// of the struct. For example, consider: @@ -51,7 +52,7 @@ public: /// /// This only gets initialized if the base subobject type is /// different from the complete-object type. - const llvm::StructType *BaseSubobjectType; + llvm::StructType *BaseSubobjectType; /// FieldInfo - Holds a field and its corresponding LLVM field number. llvm::DenseMap<const FieldDecl *, unsigned> Fields; @@ -108,8 +109,8 @@ private: /// LayoutUnionField - Will layout a field in an union and return the type /// that the field will have. - const llvm::Type *LayoutUnionField(const FieldDecl *Field, - const ASTRecordLayout &Layout); + llvm::Type *LayoutUnionField(const FieldDecl *Field, + const ASTRecordLayout &Layout); /// LayoutUnion - Will layout a union RecordDecl. void LayoutUnion(const RecordDecl *D); @@ -150,7 +151,7 @@ private: void LayoutBitField(const FieldDecl *D, uint64_t FieldOffset); /// AppendField - Appends a field with the given offset and type. - void AppendField(CharUnits fieldOffset, const llvm::Type *FieldTy); + void AppendField(CharUnits fieldOffset, llvm::Type *FieldTy); /// AppendPadding - Appends enough padding bytes so that the total /// struct size is a multiple of the field alignment. @@ -164,7 +165,7 @@ private: /// getByteArrayType - Returns a byte array type with the given number of /// elements. - const llvm::Type *getByteArrayType(CharUnits NumBytes); + llvm::Type *getByteArrayType(CharUnits NumBytes); /// AppendBytes - Append a given number of bytes to the record. void AppendBytes(CharUnits numBytes); @@ -229,7 +230,7 @@ CGBitFieldInfo CGBitFieldInfo::MakeInfo(CodeGenTypes &Types, uint64_t FieldSize, uint64_t ContainingTypeSizeInBits, unsigned ContainingTypeAlign) { - const llvm::Type *Ty = Types.ConvertTypeForMemRecursive(FD->getType()); + const llvm::Type *Ty = Types.ConvertTypeForMem(FD->getType()); CharUnits TypeSizeInBytes = CharUnits::fromQuantity(Types.getTargetData().getTypeAllocSize(Ty)); uint64_t TypeSizeInBits = Types.getContext().toBits(TypeSizeInBytes); @@ -268,6 +269,18 @@ CGBitFieldInfo CGBitFieldInfo::MakeInfo(CodeGenTypes &Types, unsigned AccessedTargetBits = 0; // The number of target bits accessed. unsigned AccessWidth = TypeSizeInBits; // The current access width to attempt. + // If requested, widen the initial bit-field access to be register sized. The + // theory is that this is most likely to allow multiple accesses into the same + // structure to be coalesced, and that the backend should be smart enough to + // narrow the store if no coalescing is ever done. + // + // The subsequent code will handle align these access to common boundaries and + // guaranteeing that we do not access past the end of the structure. + if (Types.getCodeGenOpts().UseRegisterSizedBitfieldAccess) { + if (AccessWidth < Types.getTarget().getRegisterWidth()) + AccessWidth = Types.getTarget().getRegisterWidth(); + } + // Round down from the field offset to find the first access position that is // at an aligned offset of the initial access type. uint64_t AccessStart = FieldOffset - (FieldOffset % AccessWidth); @@ -427,7 +440,7 @@ bool CGRecordLayoutBuilder::LayoutField(const FieldDecl *D, CharUnits fieldOffsetInBytes = Types.getContext().toCharUnitsFromBits(fieldOffset); - const llvm::Type *Ty = Types.ConvertTypeForMemRecursive(D->getType()); + llvm::Type *Ty = Types.ConvertTypeForMem(D->getType()); CharUnits typeAlignment = getTypeAlignment(Ty); // If the type alignment is larger then the struct alignment, we must use @@ -475,7 +488,7 @@ bool CGRecordLayoutBuilder::LayoutField(const FieldDecl *D, return true; } -const llvm::Type * +llvm::Type * CGRecordLayoutBuilder::LayoutUnionField(const FieldDecl *Field, const ASTRecordLayout &Layout) { if (Field->isBitField()) { @@ -486,7 +499,7 @@ CGRecordLayoutBuilder::LayoutUnionField(const FieldDecl *Field, if (FieldSize == 0) return 0; - const llvm::Type *FieldTy = llvm::Type::getInt8Ty(Types.getLLVMContext()); + llvm::Type *FieldTy = llvm::Type::getInt8Ty(Types.getLLVMContext()); CharUnits NumBytesToAppend = Types.getContext().toCharUnitsFromBits( llvm::RoundUpToAlignment(FieldSize, Types.getContext().Target.getCharAlign())); @@ -502,7 +515,7 @@ CGRecordLayoutBuilder::LayoutUnionField(const FieldDecl *Field, // This is a regular union field. Fields[Field] = 0; - return Types.ConvertTypeForMemRecursive(Field->getType()); + return Types.ConvertTypeForMem(Field->getType()); } void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) { @@ -510,7 +523,7 @@ void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) { const ASTRecordLayout &layout = Types.getContext().getASTRecordLayout(D); - const llvm::Type *unionType = 0; + llvm::Type *unionType = 0; CharUnits unionSize = CharUnits::Zero(); CharUnits unionAlign = CharUnits::Zero(); @@ -521,7 +534,7 @@ void CGRecordLayoutBuilder::LayoutUnion(const RecordDecl *D) { fieldEnd = D->field_end(); field != fieldEnd; ++field, ++fieldNo) { assert(layout.getFieldOffset(fieldNo) == 0 && "Union field offset did not start at the beginning of record!"); - const llvm::Type *fieldType = LayoutUnionField(*field, layout); + llvm::Type *fieldType = LayoutUnionField(*field, layout); if (!fieldType) continue; @@ -586,10 +599,8 @@ void CGRecordLayoutBuilder::LayoutBase(const CXXRecordDecl *base, // approximation, which is to use the base subobject type if it // has the same LLVM storage size as the nvsize. - const llvm::StructType *subobjectType = baseLayout.getBaseSubobjectLLVMType(); + llvm::StructType *subobjectType = baseLayout.getBaseSubobjectLLVMType(); AppendField(baseOffset, subobjectType); - - Types.addBaseSubobjectTypeName(base, baseLayout); } void CGRecordLayoutBuilder::LayoutNonVirtualBase(const CXXRecordDecl *base, @@ -723,13 +734,14 @@ CGRecordLayoutBuilder::ComputeNonVirtualBaseType(const CXXRecordDecl *RD) { FieldTypes.push_back(getByteArrayType(NumBytes)); } - BaseSubobjectType = llvm::StructType::get(Types.getLLVMContext(), - FieldTypes, Packed); + + BaseSubobjectType = llvm::StructType::createNamed(Types.getLLVMContext(), "", + FieldTypes, Packed); + Types.addRecordTypeName(RD, BaseSubobjectType, ".base"); - if (needsPadding) { - // Pull the padding back off. + // Pull the padding back off. + if (needsPadding) FieldTypes.pop_back(); - } return true; } @@ -806,7 +818,7 @@ void CGRecordLayoutBuilder::AppendTailPadding(CharUnits RecordSize) { } void CGRecordLayoutBuilder::AppendField(CharUnits fieldOffset, - const llvm::Type *fieldType) { + llvm::Type *fieldType) { CharUnits fieldSize = CharUnits::fromQuantity(Types.getTargetData().getTypeAllocSize(fieldType)); @@ -852,10 +864,10 @@ bool CGRecordLayoutBuilder::ResizeLastBaseFieldIfNecessary(CharUnits offset) { return true; } -const llvm::Type *CGRecordLayoutBuilder::getByteArrayType(CharUnits numBytes) { +llvm::Type *CGRecordLayoutBuilder::getByteArrayType(CharUnits numBytes) { assert(!numBytes.isZero() && "Empty byte arrays aren't allowed."); - const llvm::Type *Ty = llvm::Type::getInt8Ty(Types.getLLVMContext()); + llvm::Type *Ty = llvm::Type::getInt8Ty(Types.getLLVMContext()); if (numBytes > CharUnits::One()) Ty = llvm::ArrayType::get(Ty, numBytes.getQuantity()); @@ -911,17 +923,16 @@ void CGRecordLayoutBuilder::CheckZeroInitializable(QualType T) { } } -CGRecordLayout *CodeGenTypes::ComputeRecordLayout(const RecordDecl *D) { +CGRecordLayout *CodeGenTypes::ComputeRecordLayout(const RecordDecl *D, + llvm::StructType *Ty) { CGRecordLayoutBuilder Builder(*this); Builder.Layout(D); - const llvm::StructType *Ty = llvm::StructType::get(getLLVMContext(), - Builder.FieldTypes, - Builder.Packed); + Ty->setBody(Builder.FieldTypes, Builder.Packed); // If we're in C++, compute the base subobject type. - const llvm::StructType *BaseTy = 0; + llvm::StructType *BaseTy = 0; if (isa<CXXRecordDecl>(D)) { BaseTy = Builder.BaseSubobjectType; if (!BaseTy) BaseTy = Ty; diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp index a982621be79a..07bddb797265 100644 --- a/lib/CodeGen/CGStmt.cpp +++ b/lib/CodeGen/CGStmt.cpp @@ -151,6 +151,9 @@ void CodeGenFunction::EmitStmt(const Stmt *S) { case Stmt::ObjCForCollectionStmtClass: EmitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(*S)); break; + case Stmt::ObjCAutoreleasePoolStmtClass: + EmitObjCAutoreleasePoolStmt(cast<ObjCAutoreleasePoolStmt>(*S)); + break; case Stmt::CXXTryStmtClass: EmitCXXTryStmt(cast<CXXTryStmt>(*S)); @@ -764,7 +767,7 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) { } else if (RV->getType()->isAnyComplexType()) { EmitComplexExprIntoAddr(RV, ReturnValue, false); } else { - EmitAggExpr(RV, AggValueSlot::forAddr(ReturnValue, false, true)); + EmitAggExpr(RV, AggValueSlot::forAddr(ReturnValue, Qualifiers(), true)); } EmitBranchThroughCleanup(ReturnBlock); @@ -1275,11 +1278,16 @@ AddVariableConstraints(const std::string &Constraint, const Expr &AsmExpr, return Constraint; llvm::StringRef Register = Attr->getLabel(); assert(Target.isValidGCCRegisterName(Register)); - // FIXME: We should check which registers are compatible with "r" or "x". - if (Constraint != "r" && Constraint != "x") { + // We're using validateOutputConstraint here because we only care if + // this is a register constraint. + TargetInfo::ConstraintInfo Info(Constraint, ""); + if (Target.validateOutputConstraint(Info) && + !Info.allowsRegister()) { CGM.ErrorUnsupported(&Stmt, "__asm__"); return Constraint; } + // Canonicalize the register here before returning it. + Register = Target.getNormalizedGCCRegisterName(Register); return "{" + Register.str() + "}"; } @@ -1291,7 +1299,7 @@ CodeGenFunction::EmitAsmInputLValue(const AsmStmt &S, llvm::Value *Arg; if (Info.allowsRegister() || !Info.allowsMemory()) { if (!CodeGenFunction::hasAggregateLLVMType(InputType)) { - Arg = EmitLoadOfLValue(InputValue, InputType).getScalarVal(); + Arg = EmitLoadOfLValue(InputValue).getScalarVal(); } else { const llvm::Type *Ty = ConvertType(InputType); uint64_t Size = CGM.getTargetData().getTypeSizeInBits(Ty); @@ -1400,15 +1408,15 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { std::vector<LValue> ResultRegDests; std::vector<QualType> ResultRegQualTys; - std::vector<const llvm::Type *> ResultRegTypes; - std::vector<const llvm::Type *> ResultTruncRegTypes; - std::vector<const llvm::Type*> ArgTypes; + std::vector<llvm::Type *> ResultRegTypes; + std::vector<llvm::Type *> ResultTruncRegTypes; + std::vector<llvm::Type*> ArgTypes; std::vector<llvm::Value*> Args; // Keep track of inout constraints. std::string InOutConstraints; std::vector<llvm::Value*> InOutArgs; - std::vector<const llvm::Type*> InOutArgTypes; + std::vector<llvm::Type*> InOutArgTypes; for (unsigned i = 0, e = S.getNumOutputs(); i != e; i++) { TargetInfo::ConstraintInfo &Info = OutputConstraintInfos[i]; @@ -1457,7 +1465,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { ResultRegTypes.back() = ConvertType(InputTy); } } - if (const llvm::Type* AdjTy = + if (llvm::Type* AdjTy = getTargetHooks().adjustInlineAsmType(*this, OutputConstraint, ResultRegTypes.back())) ResultRegTypes.back() = AdjTy; @@ -1550,6 +1558,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { for (unsigned i = 0, e = S.getNumClobbers(); i != e; i++) { llvm::StringRef Clobber = S.getClobber(i)->getString(); + if (Clobber != "memory" && Clobber != "cc") Clobber = Target.getNormalizedGCCRegisterName(Clobber); if (i != 0 || NumConstraints != 0) @@ -1582,7 +1591,7 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { llvm::InlineAsm *IA = llvm::InlineAsm::get(FTy, AsmString, Constraints, S.isVolatile() || S.getNumOutputs() == 0); - llvm::CallInst *Result = Builder.CreateCall(IA, Args.begin(), Args.end()); + llvm::CallInst *Result = Builder.CreateCall(IA, Args); Result->addAttribute(~0, llvm::Attribute::NoUnwind); // Slap the source location of the inline asm into a !srcloc metadata on the @@ -1629,7 +1638,6 @@ void CodeGenFunction::EmitAsmStmt(const AsmStmt &S) { } } - EmitStoreThroughLValue(RValue::get(Tmp), ResultRegDests[i], - ResultRegQualTys[i]); + EmitStoreThroughLValue(RValue::get(Tmp), ResultRegDests[i]); } } diff --git a/lib/CodeGen/CGTemporaries.cpp b/lib/CodeGen/CGTemporaries.cpp index 3b4c50910bf8..0387daeb654f 100644 --- a/lib/CodeGen/CGTemporaries.cpp +++ b/lib/CodeGen/CGTemporaries.cpp @@ -16,9 +16,12 @@ using namespace clang; using namespace CodeGen; namespace { - struct DestroyTemporary { - static void Emit(CodeGenFunction &CGF, bool forEH, - const CXXDestructorDecl *dtor, llvm::Value *addr) { + struct DestroyTemporary : EHScopeStack::Cleanup { + const CXXDestructorDecl *dtor; + llvm::Value *addr; + DestroyTemporary(const CXXDestructorDecl *dtor, llvm::Value *addr) + : dtor(dtor), addr(addr) {} + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.EmitCXXDestructorCall(dtor, Dtor_Complete, /*ForVirtualBase=*/false, addr); } diff --git a/lib/CodeGen/CGVTT.cpp b/lib/CodeGen/CGVTT.cpp index aefc41e50002..cec02cdfc235 100644 --- a/lib/CodeGen/CGVTT.cpp +++ b/lib/CodeGen/CGVTT.cpp @@ -390,8 +390,7 @@ CodeGenVTables::EmitVTTDefinition(llvm::GlobalVariable *VTT, llvm::ArrayType::get(Int8PtrTy, Builder.getVTTComponents().size()); llvm::Constant *Init = - llvm::ConstantArray::get(ArrayType, Builder.getVTTComponents().data(), - Builder.getVTTComponents().size()); + llvm::ConstantArray::get(ArrayType, Builder.getVTTComponents()); VTT->setInitializer(Init); diff --git a/lib/CodeGen/CGVTables.cpp b/lib/CodeGen/CGVTables.cpp index 9ac5e67ada4d..c161b79fd3a0 100644 --- a/lib/CodeGen/CGVTables.cpp +++ b/lib/CodeGen/CGVTables.cpp @@ -2937,7 +2937,8 @@ void CodeGenVTables::MaybeEmitThunkAvailableExternally(GlobalDecl GD, // We can't emit thunks for member functions with incomplete types. const CXXMethodDecl *MD = cast<CXXMethodDecl>(GD.getDecl()); - if (CGM.getTypes().VerifyFuncTypeComplete(MD->getType().getTypePtr())) + if (!CGM.getTypes().isFuncTypeConvertible( + cast<FunctionType>(MD->getType().getTypePtr()))) return; EmitThunk(GD, Thunk, /*UseAvailableExternallyLinkage=*/true); @@ -3165,7 +3166,7 @@ CodeGenVTables::CreateVTableInitializer(const CXXRecordDecl *RD, } llvm::ArrayType *ArrayType = llvm::ArrayType::get(Int8PtrTy, NumComponents); - return llvm::ConstantArray::get(ArrayType, Inits.data(), Inits.size()); + return llvm::ConstantArray::get(ArrayType, Inits); } llvm::GlobalVariable *CodeGenVTables::GetAddrOfVTable(const CXXRecordDecl *RD) { diff --git a/lib/CodeGen/CGVTables.h b/lib/CodeGen/CGVTables.h index e830e9a6fbcb..eff6e56c1f80 100644 --- a/lib/CodeGen/CGVTables.h +++ b/lib/CodeGen/CGVTables.h @@ -18,7 +18,7 @@ #include "llvm/GlobalVariable.h" #include "clang/Basic/ABI.h" #include "clang/AST/CharUnits.h" -#include "GlobalDecl.h" +#include "clang/AST/GlobalDecl.h" namespace clang { class CXXRecordDecl; diff --git a/lib/CodeGen/CGValue.h b/lib/CodeGen/CGValue.h index 7f77d552032a..4d0b8410e451 100644 --- a/lib/CodeGen/CGValue.h +++ b/lib/CodeGen/CGValue.h @@ -101,8 +101,6 @@ public: /// bitfields, this is not a simple LLVM pointer, it may be a pointer plus a /// bitrange. class LValue { - // FIXME: alignment? - enum { Simple, // This is a normal l-value, use getAddress(). VectorElt, // This is a vector element l-value (V[i]), use getVector* @@ -128,6 +126,8 @@ class LValue { const ObjCPropertyRefExpr *PropertyRefExpr; }; + QualType Type; + // 'const' is unused here Qualifiers Quals; @@ -156,8 +156,9 @@ class LValue { llvm::MDNode *TBAAInfo; private: - void Initialize(Qualifiers Quals, unsigned Alignment = 0, + void Initialize(QualType Type, Qualifiers Quals, unsigned Alignment = 0, llvm::MDNode *TBAAInfo = 0) { + this->Type = Type; this->Quals = Quals; this->Alignment = Alignment; assert(this->Alignment == Alignment && "Alignment exceeds allowed max!"); @@ -182,6 +183,12 @@ public: return Quals.getCVRQualifiers() & ~Qualifiers::Const; } + QualType getType() const { return Type; } + + Qualifiers::ObjCLifetime getObjCLifetime() const { + return Quals.getObjCLifetime(); + } + bool isObjCIvar() const { return Ivar; } void setObjCIvar(bool Value) { Ivar = Value; } @@ -203,6 +210,10 @@ public: bool isObjCStrong() const { return Quals.getObjCGCAttr() == Qualifiers::Strong; } + + bool isVolatile() const { + return Quals.hasVolatile(); + } Expr *getBaseIvarExp() const { return BaseIvarExp; } void setBaseIvarExp(Expr *V) { BaseIvarExp = V; } @@ -219,6 +230,10 @@ public: // simple lvalue llvm::Value *getAddress() const { assert(isSimple()); return V; } + void setAddress(llvm::Value *address) { + assert(isSimple()); + V = address; + } // vector elt lvalue llvm::Value *getVectorAddr() const { assert(isVectorElt()); return V; } @@ -251,36 +266,36 @@ public: return PropertyRefExpr; } - static LValue MakeAddr(llvm::Value *V, QualType T, unsigned Alignment, - ASTContext &Context, + static LValue MakeAddr(llvm::Value *address, QualType type, + unsigned alignment, ASTContext &Context, llvm::MDNode *TBAAInfo = 0) { - Qualifiers Quals = T.getQualifiers(); - Quals.setObjCGCAttr(Context.getObjCGCAttrKind(T)); + Qualifiers qs = type.getQualifiers(); + qs.setObjCGCAttr(Context.getObjCGCAttrKind(type)); LValue R; R.LVType = Simple; - R.V = V; - R.Initialize(Quals, Alignment, TBAAInfo); + R.V = address; + R.Initialize(type, qs, alignment, TBAAInfo); return R; } static LValue MakeVectorElt(llvm::Value *Vec, llvm::Value *Idx, - unsigned CVR) { + QualType type) { LValue R; R.LVType = VectorElt; R.V = Vec; R.VectorIdx = Idx; - R.Initialize(Qualifiers::fromCVRMask(CVR)); + R.Initialize(type, type.getQualifiers()); return R; } static LValue MakeExtVectorElt(llvm::Value *Vec, llvm::Constant *Elts, - unsigned CVR) { + QualType type) { LValue R; R.LVType = ExtVectorElt; R.V = Vec; R.VectorElts = Elts; - R.Initialize(Qualifiers::fromCVRMask(CVR)); + R.Initialize(type, type.getQualifiers()); return R; } @@ -290,13 +305,14 @@ public: /// bit-field. /// \param Info - The information describing how to perform the bit-field /// access. - static LValue MakeBitfield(llvm::Value *BaseValue, const CGBitFieldInfo &Info, - unsigned CVR) { + static LValue MakeBitfield(llvm::Value *BaseValue, + const CGBitFieldInfo &Info, + QualType type) { LValue R; R.LVType = BitField; R.V = BaseValue; R.BitFieldInfo = &Info; - R.Initialize(Qualifiers::fromCVRMask(CVR)); + R.Initialize(type, type.getQualifiers()); return R; } @@ -309,7 +325,7 @@ public: R.LVType = PropertyRef; R.V = Base; R.PropertyRefExpr = E; - R.Initialize(Qualifiers()); + R.Initialize(QualType(), Qualifiers()); return R; } }; @@ -318,9 +334,11 @@ public: class AggValueSlot { /// The address. llvm::Value *Addr; + + // Qualifiers + Qualifiers Quals; // Associated flags. - bool VolatileFlag : 1; bool LifetimeFlag : 1; bool RequiresGCollection : 1; @@ -335,25 +353,31 @@ public: static AggValueSlot ignored() { AggValueSlot AV; AV.Addr = 0; - AV.VolatileFlag = AV.LifetimeFlag = AV.RequiresGCollection = AV.IsZeroed =0; + AV.Quals = Qualifiers(); + AV.LifetimeFlag = AV.RequiresGCollection = AV.IsZeroed =0; return AV; } /// forAddr - Make a slot for an aggregate value. /// /// \param Volatile - true if the slot should be volatile-initialized + /// + /// \param Qualifiers - The qualifiers that dictate how the slot + /// should be initialied. Only 'volatile' and the Objective-C + /// lifetime qualifiers matter. + /// /// \param LifetimeExternallyManaged - true if the slot's lifetime /// is being externally managed; false if a destructor should be /// registered for any temporaries evaluated into the slot /// \param RequiresGCollection - true if the slot is located /// somewhere that ObjC GC calls should be emitted for - static AggValueSlot forAddr(llvm::Value *Addr, bool Volatile, + static AggValueSlot forAddr(llvm::Value *Addr, Qualifiers Quals, bool LifetimeExternallyManaged, bool RequiresGCollection = false, bool IsZeroed = false) { AggValueSlot AV; AV.Addr = Addr; - AV.VolatileFlag = Volatile; + AV.Quals = Quals; AV.LifetimeFlag = LifetimeExternallyManaged; AV.RequiresGCollection = RequiresGCollection; AV.IsZeroed = IsZeroed; @@ -361,9 +385,10 @@ public: } static AggValueSlot forLValue(LValue LV, bool LifetimeExternallyManaged, - bool RequiresGCollection = false) { - return forAddr(LV.getAddress(), LV.isVolatileQualified(), - LifetimeExternallyManaged, RequiresGCollection); + bool RequiresGCollection = false, + bool IsZeroed = false) { + return forAddr(LV.getAddress(), LV.getQuals(), + LifetimeExternallyManaged, RequiresGCollection, IsZeroed); } bool isLifetimeExternallyManaged() const { @@ -373,8 +398,14 @@ public: LifetimeFlag = Managed; } + Qualifiers getQualifiers() const { return Quals; } + bool isVolatile() const { - return VolatileFlag; + return Quals.hasVolatile(); + } + + Qualifiers::ObjCLifetime getObjCLifetime() const { + return Quals.getObjCLifetime(); } bool requiresGCollection() const { diff --git a/lib/CodeGen/CodeGenAction.cpp b/lib/CodeGen/CodeGenAction.cpp index 62fa1f9843de..263e01e4f18a 100644 --- a/lib/CodeGen/CodeGenAction.cpp +++ b/lib/CodeGen/CodeGenAction.cpp @@ -34,6 +34,7 @@ namespace clang { BackendAction Action; const CodeGenOptions &CodeGenOpts; const TargetOptions &TargetOpts; + const LangOptions &LangOpts; llvm::raw_ostream *AsmOutStream; ASTContext *Context; @@ -46,13 +47,16 @@ namespace clang { public: BackendConsumer(BackendAction action, Diagnostic &_Diags, const CodeGenOptions &compopts, - const TargetOptions &targetopts, bool TimePasses, + const TargetOptions &targetopts, + const LangOptions &langopts, + bool TimePasses, const std::string &infile, llvm::raw_ostream *OS, LLVMContext &C) : Diags(_Diags), Action(action), CodeGenOpts(compopts), TargetOpts(targetopts), + LangOpts(langopts), AsmOutStream(OS), LLVMIRGeneration("LLVM IR Generation Time"), Gen(CreateLLVMCodeGen(Diags, infile, compopts, C)) { @@ -126,7 +130,7 @@ namespace clang { void *OldContext = Ctx.getInlineAsmDiagnosticContext(); Ctx.setInlineAsmDiagnosticHandler(InlineAsmDiagHandler, this); - EmitBackendOutput(Diags, CodeGenOpts, TargetOpts, + EmitBackendOutput(Diags, CodeGenOpts, TargetOpts, LangOpts, TheModule.get(), Action, AsmOutStream); Ctx.setInlineAsmDiagnosticHandler(OldHandler, OldContext); @@ -285,6 +289,7 @@ ASTConsumer *CodeGenAction::CreateASTConsumer(CompilerInstance &CI, BEConsumer = new BackendConsumer(BA, CI.getDiagnostics(), CI.getCodeGenOpts(), CI.getTargetOpts(), + CI.getLangOpts(), CI.getFrontendOpts().ShowTimers, InFile, OS.take(), *VMContext); return BEConsumer; @@ -332,7 +337,8 @@ void CodeGenAction::ExecuteAction() { } EmitBackendOutput(CI.getDiagnostics(), CI.getCodeGenOpts(), - CI.getTargetOpts(), TheModule.get(), + CI.getTargetOpts(), CI.getLangOpts(), + TheModule.get(), BA, OS); return; } diff --git a/lib/CodeGen/CodeGenFunction.cpp b/lib/CodeGen/CodeGenFunction.cpp index 150cb69b4d1a..702897a7c448 100644 --- a/lib/CodeGen/CodeGenFunction.cpp +++ b/lib/CodeGen/CodeGenFunction.cpp @@ -31,7 +31,7 @@ using namespace CodeGen; CodeGenFunction::CodeGenFunction(CodeGenModule &cgm) : CodeGenTypeCache(cgm), CGM(cgm), Target(CGM.getContext().Target), Builder(cgm.getModule().getContext()), - BlockInfo(0), BlockPointer(0), + AutoreleaseResult(false), BlockInfo(0), BlockPointer(0), NormalCleanupDest(0), EHCleanupDest(0), NextCleanupDestIndex(1), ExceptionSlot(0), EHSelectorSlot(0), DebugInfo(0), DisableDebugInfo(false), DidCallStackSave(false), @@ -45,11 +45,11 @@ CodeGenFunction::CodeGenFunction(CodeGenModule &cgm) } -const llvm::Type *CodeGenFunction::ConvertTypeForMem(QualType T) { +llvm::Type *CodeGenFunction::ConvertTypeForMem(QualType T) { return CGM.getTypes().ConvertTypeForMem(T); } -const llvm::Type *CodeGenFunction::ConvertType(QualType T) { +llvm::Type *CodeGenFunction::ConvertType(QualType T) { return CGM.getTypes().ConvertType(T); } @@ -142,6 +142,13 @@ void CodeGenFunction::FinishFunction(SourceLocation EndLoc) { assert(BreakContinueStack.empty() && "mismatched push/pop in break/continue stack!"); + // Pop any cleanups that might have been associated with the + // parameters. Do this in whatever block we're currently in; it's + // important to do this before we enter the return block or return + // edges will be *really* confused. + if (EHStack.stable_begin() != PrologueCleanupDepth) + PopCleanupBlocks(PrologueCleanupDepth); + // Emit function epilog (to return). EmitReturnBlock(); @@ -206,15 +213,15 @@ bool CodeGenFunction::ShouldInstrumentFunction() { /// function instrumentation is enabled. void CodeGenFunction::EmitFunctionInstrumentation(const char *Fn) { // void __cyg_profile_func_{enter,exit} (void *this_fn, void *call_site); - const llvm::PointerType *PointerTy = Int8PtrTy; - const llvm::Type *ProfileFuncArgs[] = { PointerTy, PointerTy }; + llvm::PointerType *PointerTy = Int8PtrTy; + llvm::Type *ProfileFuncArgs[] = { PointerTy, PointerTy }; const llvm::FunctionType *FunctionTy = llvm::FunctionType::get(llvm::Type::getVoidTy(getLLVMContext()), ProfileFuncArgs, false); llvm::Constant *F = CGM.CreateRuntimeFunction(FunctionTy, Fn); llvm::CallInst *CallSite = Builder.CreateCall( - CGM.getIntrinsic(llvm::Intrinsic::returnaddress, 0, 0), + CGM.getIntrinsic(llvm::Intrinsic::returnaddress), llvm::ConstantInt::get(Int32Ty, 0), "callsite"); @@ -311,9 +318,19 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, ReturnValue = CurFn->arg_begin(); } else { ReturnValue = CreateIRTemp(RetTy, "retval"); + + // Tell the epilog emitter to autorelease the result. We do this + // now so that various specialized functions can suppress it + // during their IR-generation. + if (getLangOptions().ObjCAutoRefCount && + !CurFnInfo->isReturnsRetained() && + RetTy->isObjCRetainableType()) + AutoreleaseResult = true; } EmitStartEHSpec(CurCodeDecl); + + PrologueCleanupDepth = EHStack.stable_begin(); EmitFunctionProlog(*CurFnInfo, CurFn, Args); if (D && isa<CXXMethodDecl>(D) && cast<CXXMethodDecl>(D)->isInstance()) @@ -326,7 +343,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, QualType Ty = (*i)->getType(); if (Ty->isVariablyModifiedType()) - EmitVLASize(Ty); + EmitVariablyModifiedType(Ty); } } @@ -692,13 +709,20 @@ CodeGenFunction::EmitNullInitialization(llvm::Value *DestPtr, QualType Ty) { if (const VariableArrayType *vlaType = dyn_cast_or_null<VariableArrayType>( getContext().getAsArrayType(Ty))) { - SizeVal = GetVLASize(vlaType); + QualType eltType; + llvm::Value *numElts; + llvm::tie(numElts, eltType) = getVLASize(vlaType); + + SizeVal = numElts; + CharUnits eltSize = getContext().getTypeSizeInChars(eltType); + if (!eltSize.isOne()) + SizeVal = Builder.CreateNUWMul(SizeVal, CGM.getSize(eltSize)); vla = vlaType; } else { return; } } else { - SizeVal = llvm::ConstantInt::get(IntPtrTy, Size.getQuantity()); + SizeVal = CGM.getSize(Size); vla = 0; } @@ -761,60 +785,198 @@ llvm::BasicBlock *CodeGenFunction::GetIndirectGotoBlock() { return IndirectBranch->getParent(); } -llvm::Value *CodeGenFunction::GetVLASize(const VariableArrayType *VAT) { - llvm::Value *&SizeEntry = VLASizeMap[VAT->getSizeExpr()]; +/// Computes the length of an array in elements, as well as the base +/// element type and a properly-typed first element pointer. +llvm::Value *CodeGenFunction::emitArrayLength(const ArrayType *origArrayType, + QualType &baseType, + llvm::Value *&addr) { + const ArrayType *arrayType = origArrayType; + + // If it's a VLA, we have to load the stored size. Note that + // this is the size of the VLA in bytes, not its size in elements. + llvm::Value *numVLAElements = 0; + if (isa<VariableArrayType>(arrayType)) { + numVLAElements = getVLASize(cast<VariableArrayType>(arrayType)).first; + + // Walk into all VLAs. This doesn't require changes to addr, + // which has type T* where T is the first non-VLA element type. + do { + QualType elementType = arrayType->getElementType(); + arrayType = getContext().getAsArrayType(elementType); + + // If we only have VLA components, 'addr' requires no adjustment. + if (!arrayType) { + baseType = elementType; + return numVLAElements; + } + } while (isa<VariableArrayType>(arrayType)); - assert(SizeEntry && "Did not emit size for type"); - return SizeEntry; -} + // We get out here only if we find a constant array type + // inside the VLA. + } -llvm::Value *CodeGenFunction::EmitVLASize(QualType Ty) { - assert(Ty->isVariablyModifiedType() && - "Must pass variably modified type to EmitVLASizes!"); + // We have some number of constant-length arrays, so addr should + // have LLVM type [M x [N x [...]]]*. Build a GEP that walks + // down to the first element of addr. + llvm::SmallVector<llvm::Value*, 8> gepIndices; - EnsureInsertPoint(); + // GEP down to the array type. + llvm::ConstantInt *zero = Builder.getInt32(0); + gepIndices.push_back(zero); + + // It's more efficient to calculate the count from the LLVM + // constant-length arrays than to re-evaluate the array bounds. + uint64_t countFromCLAs = 1; + + const llvm::ArrayType *llvmArrayType = + cast<llvm::ArrayType>( + cast<llvm::PointerType>(addr->getType())->getElementType()); + while (true) { + assert(isa<ConstantArrayType>(arrayType)); + assert(cast<ConstantArrayType>(arrayType)->getSize().getZExtValue() + == llvmArrayType->getNumElements()); + + gepIndices.push_back(zero); + countFromCLAs *= llvmArrayType->getNumElements(); + + llvmArrayType = + dyn_cast<llvm::ArrayType>(llvmArrayType->getElementType()); + if (!llvmArrayType) break; + + arrayType = getContext().getAsArrayType(arrayType->getElementType()); + assert(arrayType && "LLVM and Clang types are out-of-synch"); + } + + baseType = arrayType->getElementType(); + + // Create the actual GEP. + addr = Builder.CreateInBoundsGEP(addr, gepIndices.begin(), + gepIndices.end(), "array.begin"); - if (const VariableArrayType *VAT = getContext().getAsVariableArrayType(Ty)) { - // unknown size indication requires no size computation. - if (!VAT->getSizeExpr()) - return 0; - llvm::Value *&SizeEntry = VLASizeMap[VAT->getSizeExpr()]; + llvm::Value *numElements + = llvm::ConstantInt::get(SizeTy, countFromCLAs); - if (!SizeEntry) { - const llvm::Type *SizeTy = ConvertType(getContext().getSizeType()); + // If we had any VLA dimensions, factor them in. + if (numVLAElements) + numElements = Builder.CreateNUWMul(numVLAElements, numElements); - // Get the element size; - QualType ElemTy = VAT->getElementType(); - llvm::Value *ElemSize; - if (ElemTy->isVariableArrayType()) - ElemSize = EmitVLASize(ElemTy); - else - ElemSize = llvm::ConstantInt::get(SizeTy, - getContext().getTypeSizeInChars(ElemTy).getQuantity()); + return numElements; +} + +std::pair<llvm::Value*, QualType> +CodeGenFunction::getVLASize(QualType type) { + const VariableArrayType *vla = getContext().getAsVariableArrayType(type); + assert(vla && "type was not a variable array type!"); + return getVLASize(vla); +} + +std::pair<llvm::Value*, QualType> +CodeGenFunction::getVLASize(const VariableArrayType *type) { + // The number of elements so far; always size_t. + llvm::Value *numElements = 0; - llvm::Value *NumElements = EmitScalarExpr(VAT->getSizeExpr()); - NumElements = Builder.CreateIntCast(NumElements, SizeTy, false, "tmp"); + QualType elementType; + do { + elementType = type->getElementType(); + llvm::Value *vlaSize = VLASizeMap[type->getSizeExpr()]; + assert(vlaSize && "no size for VLA!"); + assert(vlaSize->getType() == SizeTy); - SizeEntry = Builder.CreateMul(ElemSize, NumElements); + if (!numElements) { + numElements = vlaSize; + } else { + // It's undefined behavior if this wraps around, so mark it that way. + numElements = Builder.CreateNUWMul(numElements, vlaSize); } + } while ((type = getContext().getAsVariableArrayType(elementType))); - return SizeEntry; - } + return std::pair<llvm::Value*,QualType>(numElements, elementType); +} - if (const ArrayType *AT = dyn_cast<ArrayType>(Ty)) { - EmitVLASize(AT->getElementType()); - return 0; - } +void CodeGenFunction::EmitVariablyModifiedType(QualType type) { + assert(type->isVariablyModifiedType() && + "Must pass variably modified type to EmitVLASizes!"); - if (const ParenType *PT = dyn_cast<ParenType>(Ty)) { - EmitVLASize(PT->getInnerType()); - return 0; - } + EnsureInsertPoint(); + + // We're going to walk down into the type and look for VLA + // expressions. + type = type.getCanonicalType(); + do { + assert(type->isVariablyModifiedType()); + + const Type *ty = type.getTypePtr(); + switch (ty->getTypeClass()) { +#define TYPE(Class, Base) +#define ABSTRACT_TYPE(Class, Base) +#define NON_CANONICAL_TYPE(Class, Base) case Type::Class: +#define DEPENDENT_TYPE(Class, Base) case Type::Class: +#define NON_CANONICAL_UNLESS_DEPENDENT_TYPE(Class, Base) case Type::Class: +#include "clang/AST/TypeNodes.def" + llvm_unreachable("unexpected dependent or non-canonical type!"); + + // These types are never variably-modified. + case Type::Builtin: + case Type::Complex: + case Type::Vector: + case Type::ExtVector: + case Type::Record: + case Type::Enum: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: + llvm_unreachable("type class is never variably-modified!"); + + case Type::Pointer: + type = cast<PointerType>(ty)->getPointeeType(); + break; + + case Type::BlockPointer: + type = cast<BlockPointerType>(ty)->getPointeeType(); + break; + + case Type::LValueReference: + case Type::RValueReference: + type = cast<ReferenceType>(ty)->getPointeeType(); + break; + + case Type::MemberPointer: + type = cast<MemberPointerType>(ty)->getPointeeType(); + break; + + case Type::ConstantArray: + case Type::IncompleteArray: + // Losing element qualification here is fine. + type = cast<ArrayType>(ty)->getElementType(); + break; + + case Type::VariableArray: { + // Losing element qualification here is fine. + const VariableArrayType *vat = cast<VariableArrayType>(ty); + + // Unknown size indication requires no size computation. + // Otherwise, evaluate and record it. + if (const Expr *size = vat->getSizeExpr()) { + // It's possible that we might have emitted this already, + // e.g. with a typedef and a pointer to it. + llvm::Value *&entry = VLASizeMap[size]; + if (!entry) { + // Always zexting here would be wrong if it weren't + // undefined behavior to have a negative bound. + entry = Builder.CreateIntCast(EmitScalarExpr(size), SizeTy, + /*signed*/ false); + } + } + type = vat->getElementType(); + break; + } - const PointerType *PT = Ty->getAs<PointerType>(); - assert(PT && "unknown VM type!"); - EmitVLASize(PT->getPointeeType()); - return 0; + case Type::FunctionProto: + case Type::FunctionNoProto: + type = cast<FunctionType>(ty)->getResultType(); + break; + } + } while (type->isVariablyModifiedType()); } llvm::Value* CodeGenFunction::EmitVAListRef(const Expr* E) { diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index bb8fd8e24306..f27ed947b8d8 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -18,8 +18,10 @@ #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/CharUnits.h" +#include "clang/Frontend/CodeGenOptions.h" #include "clang/Basic/ABI.h" #include "clang/Basic/TargetInfo.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/ValueHandle.h" @@ -63,6 +65,7 @@ namespace clang { class ObjCAtTryStmt; class ObjCAtThrowStmt; class ObjCAtSynchronizedStmt; + class ObjCAutoreleasePoolStmt; namespace CodeGen { class CodeGenTypes; @@ -179,15 +182,39 @@ public: /// Cleanup implementations should generally be declared in an /// anonymous namespace. class Cleanup { + // Anchor the construction vtable. + virtual void anchor(); public: - // Anchor the construction vtable. We use the destructor because - // gcc gives an obnoxious warning if there are virtual methods - // with an accessible non-virtual destructor. Unfortunately, - // declaring this destructor makes it non-trivial, but there - // doesn't seem to be any other way around this warning. - // - // This destructor will never be called. - virtual ~Cleanup(); + /// Generation flags. + class Flags { + enum { + F_IsForEH = 0x1, + F_IsNormalCleanupKind = 0x2, + F_IsEHCleanupKind = 0x4 + }; + unsigned flags; + + public: + Flags() : flags(0) {} + + /// isForEH - true if the current emission is for an EH cleanup. + bool isForEHCleanup() const { return flags & F_IsForEH; } + bool isForNormalCleanup() const { return !isForEHCleanup(); } + void setIsForEHCleanup() { flags |= F_IsForEH; } + + bool isNormalCleanupKind() const { return flags & F_IsNormalCleanupKind; } + void setIsNormalCleanupKind() { flags |= F_IsNormalCleanupKind; } + + /// isEHCleanupKind - true if the cleanup was pushed as an EH + /// cleanup. + bool isEHCleanupKind() const { return flags & F_IsEHCleanupKind; } + void setIsEHCleanupKind() { flags |= F_IsEHCleanupKind; } + }; + + // Provide a virtual destructor to suppress a very common warning + // that unfortunately cannot be suppressed without this. Cleanups + // should not rely on this destructor ever being called. + virtual ~Cleanup() {} /// Emit the cleanup. For normal cleanups, this is run in the /// same EH context as when the cleanup was pushed, i.e. the @@ -196,29 +223,7 @@ public: /// // \param IsForEHCleanup true if this is for an EH cleanup, false /// if for a normal cleanup. - virtual void Emit(CodeGenFunction &CGF, bool IsForEHCleanup) = 0; - }; - - /// UnconditionalCleanupN stores its N parameters and just passes - /// them to the real cleanup function. - template <class T, class A0> - class UnconditionalCleanup1 : public Cleanup { - A0 a0; - public: - UnconditionalCleanup1(A0 a0) : a0(a0) {} - void Emit(CodeGenFunction &CGF, bool IsForEHCleanup) { - T::Emit(CGF, IsForEHCleanup, a0); - } - }; - - template <class T, class A0, class A1> - class UnconditionalCleanup2 : public Cleanup { - A0 a0; A1 a1; - public: - UnconditionalCleanup2(A0 a0, A1 a1) : a0(a0), a1(a1) {} - void Emit(CodeGenFunction &CGF, bool IsForEHCleanup) { - T::Emit(CGF, IsForEHCleanup, a0, a1); - } + virtual void Emit(CodeGenFunction &CGF, Flags flags) = 0; }; /// ConditionalCleanupN stores the saved form of its N parameters, @@ -228,9 +233,9 @@ public: typedef typename DominatingValue<A0>::saved_type A0_saved; A0_saved a0_saved; - void Emit(CodeGenFunction &CGF, bool IsForEHCleanup) { + void Emit(CodeGenFunction &CGF, Flags flags) { A0 a0 = DominatingValue<A0>::restore(CGF, a0_saved); - T::Emit(CGF, IsForEHCleanup, a0); + T(a0).Emit(CGF, flags); } public: @@ -245,10 +250,10 @@ public: A0_saved a0_saved; A1_saved a1_saved; - void Emit(CodeGenFunction &CGF, bool IsForEHCleanup) { + void Emit(CodeGenFunction &CGF, Flags flags) { A0 a0 = DominatingValue<A0>::restore(CGF, a0_saved); A1 a1 = DominatingValue<A1>::restore(CGF, a1_saved); - T::Emit(CGF, IsForEHCleanup, a0, a1); + T(a0, a1).Emit(CGF, flags); } public: @@ -256,6 +261,51 @@ public: : a0_saved(a0), a1_saved(a1) {} }; + template <class T, class A0, class A1, class A2> + class ConditionalCleanup3 : public Cleanup { + typedef typename DominatingValue<A0>::saved_type A0_saved; + typedef typename DominatingValue<A1>::saved_type A1_saved; + typedef typename DominatingValue<A2>::saved_type A2_saved; + A0_saved a0_saved; + A1_saved a1_saved; + A2_saved a2_saved; + + void Emit(CodeGenFunction &CGF, Flags flags) { + A0 a0 = DominatingValue<A0>::restore(CGF, a0_saved); + A1 a1 = DominatingValue<A1>::restore(CGF, a1_saved); + A2 a2 = DominatingValue<A2>::restore(CGF, a2_saved); + T(a0, a1, a2).Emit(CGF, flags); + } + + public: + ConditionalCleanup3(A0_saved a0, A1_saved a1, A2_saved a2) + : a0_saved(a0), a1_saved(a1), a2_saved(a2) {} + }; + + template <class T, class A0, class A1, class A2, class A3> + class ConditionalCleanup4 : public Cleanup { + typedef typename DominatingValue<A0>::saved_type A0_saved; + typedef typename DominatingValue<A1>::saved_type A1_saved; + typedef typename DominatingValue<A2>::saved_type A2_saved; + typedef typename DominatingValue<A3>::saved_type A3_saved; + A0_saved a0_saved; + A1_saved a1_saved; + A2_saved a2_saved; + A3_saved a3_saved; + + void Emit(CodeGenFunction &CGF, Flags flags) { + A0 a0 = DominatingValue<A0>::restore(CGF, a0_saved); + A1 a1 = DominatingValue<A1>::restore(CGF, a1_saved); + A2 a2 = DominatingValue<A2>::restore(CGF, a2_saved); + A3 a3 = DominatingValue<A3>::restore(CGF, a3_saved); + T(a0, a1, a2, a3).Emit(CGF, flags); + } + + public: + ConditionalCleanup4(A0_saved a0, A1_saved a1, A2_saved a2, A3_saved a3) + : a0_saved(a0), a1_saved(a1), a2_saved(a2), a3_saved(a3) {} + }; + private: // The implementation for this class is in CGException.h and // CGException.cpp; the definition is here because it's used as a @@ -568,6 +618,10 @@ public: /// CurGD - The GlobalDecl for the current function being compiled. GlobalDecl CurGD; + /// PrologueCleanupDepth - The cleanup depth enclosing all the + /// cleanups associated with the parameters. + EHScopeStack::stable_iterator PrologueCleanupDepth; + /// ReturnBlock - Unified return block. JumpDest ReturnBlock; @@ -584,6 +638,9 @@ public: bool CatchUndefined; + /// In ARC, whether we should autorelease the return value. + bool AutoreleaseResult; + const CodeGen::CGBlockInfo *BlockInfo; llvm::Value *BlockPointer; @@ -626,16 +683,28 @@ public: /// rethrows. llvm::SmallVector<llvm::Value*, 8> ObjCEHValueStack; - // A struct holding information about a finally block's IR - // generation. For now, doesn't actually hold anything. - struct FinallyInfo { - }; + /// A class controlling the emission of a finally block. + class FinallyInfo { + /// Where the catchall's edge through the cleanup should go. + JumpDest RethrowDest; + + /// A function to call to enter the catch. + llvm::Constant *BeginCatchFn; - FinallyInfo EnterFinallyBlock(const Stmt *Stmt, - llvm::Constant *BeginCatchFn, - llvm::Constant *EndCatchFn, - llvm::Constant *RethrowFn); - void ExitFinallyBlock(FinallyInfo &FinallyInfo); + /// An i1 variable indicating whether or not the @finally is + /// running for an exception. + llvm::AllocaInst *ForEHVar; + + /// An i8* variable into which the exception pointer to rethrow + /// has been saved. + llvm::AllocaInst *SavedExnVar; + + public: + void enter(CodeGenFunction &CGF, const Stmt *Finally, + llvm::Constant *beginCatchFn, llvm::Constant *endCatchFn, + llvm::Constant *rethrowFn); + void exit(CodeGenFunction &CGF); + }; /// pushFullExprCleanup - Push a cleanup to be run at the end of the /// current full-expression. Safe against the possibility that @@ -644,10 +713,8 @@ public: void pushFullExprCleanup(CleanupKind kind, A0 a0) { // If we're not in a conditional branch, or if none of the // arguments requires saving, then use the unconditional cleanup. - if (!isInConditionalBranch()) { - typedef EHScopeStack::UnconditionalCleanup1<T, A0> CleanupType; - return EHStack.pushCleanup<CleanupType>(kind, a0); - } + if (!isInConditionalBranch()) + return EHStack.pushCleanup<T>(kind, a0); typename DominatingValue<A0>::saved_type a0_saved = saveValueInCond(a0); @@ -663,10 +730,8 @@ public: void pushFullExprCleanup(CleanupKind kind, A0 a0, A1 a1) { // If we're not in a conditional branch, or if none of the // arguments requires saving, then use the unconditional cleanup. - if (!isInConditionalBranch()) { - typedef EHScopeStack::UnconditionalCleanup2<T, A0, A1> CleanupType; - return EHStack.pushCleanup<CleanupType>(kind, a0, a1); - } + if (!isInConditionalBranch()) + return EHStack.pushCleanup<T>(kind, a0, a1); typename DominatingValue<A0>::saved_type a0_saved = saveValueInCond(a0); typename DominatingValue<A1>::saved_type a1_saved = saveValueInCond(a1); @@ -676,6 +741,48 @@ public: initFullExprCleanup(); } + /// pushFullExprCleanup - Push a cleanup to be run at the end of the + /// current full-expression. Safe against the possibility that + /// we're currently inside a conditionally-evaluated expression. + template <class T, class A0, class A1, class A2> + void pushFullExprCleanup(CleanupKind kind, A0 a0, A1 a1, A2 a2) { + // If we're not in a conditional branch, or if none of the + // arguments requires saving, then use the unconditional cleanup. + if (!isInConditionalBranch()) { + return EHStack.pushCleanup<T>(kind, a0, a1, a2); + } + + typename DominatingValue<A0>::saved_type a0_saved = saveValueInCond(a0); + typename DominatingValue<A1>::saved_type a1_saved = saveValueInCond(a1); + typename DominatingValue<A2>::saved_type a2_saved = saveValueInCond(a2); + + typedef EHScopeStack::ConditionalCleanup3<T, A0, A1, A2> CleanupType; + EHStack.pushCleanup<CleanupType>(kind, a0_saved, a1_saved, a2_saved); + initFullExprCleanup(); + } + + /// pushFullExprCleanup - Push a cleanup to be run at the end of the + /// current full-expression. Safe against the possibility that + /// we're currently inside a conditionally-evaluated expression. + template <class T, class A0, class A1, class A2, class A3> + void pushFullExprCleanup(CleanupKind kind, A0 a0, A1 a1, A2 a2, A3 a3) { + // If we're not in a conditional branch, or if none of the + // arguments requires saving, then use the unconditional cleanup. + if (!isInConditionalBranch()) { + return EHStack.pushCleanup<T>(kind, a0, a1, a2, a3); + } + + typename DominatingValue<A0>::saved_type a0_saved = saveValueInCond(a0); + typename DominatingValue<A1>::saved_type a1_saved = saveValueInCond(a1); + typename DominatingValue<A2>::saved_type a2_saved = saveValueInCond(a2); + typename DominatingValue<A3>::saved_type a3_saved = saveValueInCond(a3); + + typedef EHScopeStack::ConditionalCleanup4<T, A0, A1, A2, A3> CleanupType; + EHStack.pushCleanup<CleanupType>(kind, a0_saved, a1_saved, + a2_saved, a3_saved); + initFullExprCleanup(); + } + /// PushDestructorCleanup - Push a cleanup to call the /// complete-object destructor of an object of the given type at the /// given address. Does nothing if T is not a C++ class type with a @@ -1048,6 +1155,9 @@ public: void disableDebugInfo() { DisableDebugInfo = true; } void enableDebugInfo() { DisableDebugInfo = false; } + bool shouldUseFusedARCCalls() { + return CGM.getCodeGenOpts().OptimizationLevel == 0; + } const LangOptions &getLangOptions() const { return CGM.getLangOptions(); } @@ -1075,6 +1185,57 @@ public: llvm::LLVMContext &getLLVMContext() { return CGM.getLLVMContext(); } //===--------------------------------------------------------------------===// + // Cleanups + //===--------------------------------------------------------------------===// + + typedef void Destroyer(CodeGenFunction &CGF, llvm::Value *addr, QualType ty); + + void pushIrregularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEndPointer, + QualType elementType, + Destroyer &destroyer); + void pushRegularPartialArrayCleanup(llvm::Value *arrayBegin, + llvm::Value *arrayEnd, + QualType elementType, + Destroyer &destroyer); + + void pushDestroy(QualType::DestructionKind dtorKind, + llvm::Value *addr, QualType type); + void pushDestroy(CleanupKind kind, llvm::Value *addr, QualType type, + Destroyer &destroyer, bool useEHCleanupForArray); + void emitDestroy(llvm::Value *addr, QualType type, Destroyer &destroyer, + bool useEHCleanupForArray); + llvm::Function *generateDestroyHelper(llvm::Constant *addr, + QualType type, + Destroyer &destroyer, + bool useEHCleanupForArray); + void emitArrayDestroy(llvm::Value *begin, llvm::Value *end, + QualType type, Destroyer &destroyer, + bool checkZeroLength, bool useEHCleanup); + + Destroyer &getDestroyer(QualType::DestructionKind destructionKind); + + /// Determines whether an EH cleanup is required to destroy a type + /// with the given destruction kind. + bool needsEHCleanup(QualType::DestructionKind kind) { + switch (kind) { + case QualType::DK_none: + return false; + case QualType::DK_cxx_destructor: + case QualType::DK_objc_weak_lifetime: + return getLangOptions().Exceptions; + case QualType::DK_objc_strong_lifetime: + return getLangOptions().Exceptions && + CGM.getCodeGenOpts().ObjCAutoRefCountExceptions; + } + llvm_unreachable("bad destruction kind"); + } + + CleanupKind getCleanupKind(QualType::DestructionKind kind) { + return (needsEHCleanup(kind) ? NormalAndEHCleanup : NormalCleanup); + } + + //===--------------------------------------------------------------------===// // Objective-C //===--------------------------------------------------------------------===// @@ -1236,9 +1397,9 @@ public: /// a terminate scope encloses a try. llvm::BasicBlock *getTerminateHandler(); - const llvm::Type *ConvertTypeForMem(QualType T); - const llvm::Type *ConvertType(QualType T); - const llvm::Type *ConvertType(const TypeDecl *T) { + llvm::Type *ConvertTypeForMem(QualType T); + llvm::Type *ConvertType(QualType T); + llvm::Type *ConvertType(const TypeDecl *T) { return ConvertType(getContext().getTypeDeclType(T)); } @@ -1345,7 +1506,8 @@ public: /// CreateAggTemp - Create a temporary memory object for the given /// aggregate type. AggValueSlot CreateAggTemp(QualType T, const llvm::Twine &Name = "tmp") { - return AggValueSlot::forAddr(CreateMemTemp(T, Name), false, false); + return AggValueSlot::forAddr(CreateMemTemp(T, Name), T.getQualifiers(), + false); } /// Emit a cast to void* in the appropriate address space. @@ -1379,14 +1541,12 @@ public: /// EmitAnyExprToMem - Emits the code necessary to evaluate an /// arbitrary expression into the given memory location. void EmitAnyExprToMem(const Expr *E, llvm::Value *Location, - bool IsLocationVolatile, - bool IsInitializer); + Qualifiers Quals, bool IsInitializer); /// EmitExprAsInit - Emits the code necessary to initialize a /// location in memory with the given initializer. - void EmitExprAsInit(const Expr *init, const VarDecl *var, - llvm::Value *loc, CharUnits alignment, - bool capturedByInit); + void EmitExprAsInit(const Expr *init, const ValueDecl *D, + LValue lvalue, bool capturedByInit); /// EmitAggregateCopy - Emit an aggrate copy. /// @@ -1451,16 +1611,24 @@ public: // instruction in LLVM instead once it works well enough. llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty); - /// EmitVLASize - Generate code for any VLA size expressions that might occur - /// in a variably modified type. If Ty is a VLA, will return the value that - /// corresponds to the size in bytes of the VLA type. Will return 0 otherwise. + /// emitArrayLength - Compute the length of an array, even if it's a + /// VLA, and drill down to the base element type. + llvm::Value *emitArrayLength(const ArrayType *arrayType, + QualType &baseType, + llvm::Value *&addr); + + /// EmitVLASize - Capture all the sizes for the VLA expressions in + /// the given variably-modified type and store them in the VLASizeMap. /// /// This function can be called with a null (unreachable) insert point. - llvm::Value *EmitVLASize(QualType Ty); + void EmitVariablyModifiedType(QualType Ty); - // GetVLASize - Returns an LLVM value that corresponds to the size in bytes - // of a variable length array type. - llvm::Value *GetVLASize(const VariableArrayType *); + /// getVLASize - Returns an LLVM value that corresponds to the size, + /// in non-variably-sized elements, of a variable length array type, + /// plus that largest non-variably-sized element type. Assumes that + /// the type has already been emitted with EmitVariablyModifiedType. + std::pair<llvm::Value*,QualType> getVLASize(const VariableArrayType *vla); + std::pair<llvm::Value*,QualType> getVLASize(QualType vla); /// LoadCXXThis - Load the value of 'this'. This function is only valid while /// generating code for an C++ member function. @@ -1535,17 +1703,7 @@ public: CallExpr::const_arg_iterator ArgEnd, bool ZeroInitialization = false); - void EmitCXXAggrDestructorCall(const CXXDestructorDecl *D, - const ArrayType *Array, - llvm::Value *This); - - void EmitCXXAggrDestructorCall(const CXXDestructorDecl *D, - llvm::Value *NumElements, - llvm::Value *This); - - llvm::Function *GenerateCXXAggrDestructorHelper(const CXXDestructorDecl *D, - const ArrayType *Array, - llvm::Value *This); + static Destroyer destroyCXXObject; void EmitCXXDestructorCall(const CXXDestructorDecl *D, CXXDtorType Type, bool ForVirtualBase, llvm::Value *This); @@ -1584,6 +1742,10 @@ public: /// This function can be called with a null (unreachable) insert point. void EmitVarDecl(const VarDecl &D); + void EmitScalarInit(const Expr *init, const ValueDecl *D, + LValue lvalue, bool capturedByInit); + void EmitScalarInit(llvm::Value *init, LValue lvalue); + typedef void SpecialInitFn(CodeGenFunction &Init, const VarDecl &D, llvm::Value *Address); @@ -1639,6 +1801,8 @@ public: AutoVarEmission EmitAutoVarAlloca(const VarDecl &var); void EmitAutoVarInit(const AutoVarEmission &emission); void EmitAutoVarCleanups(const AutoVarEmission &emission); + void emitAutoVarTypeCleanup(const AutoVarEmission &emission, + QualType::DestructionKind dtorKind); void EmitStaticVarDecl(const VarDecl &D, llvm::GlobalValue::LinkageTypes Linkage); @@ -1709,6 +1873,7 @@ public: void EmitObjCAtTryStmt(const ObjCAtTryStmt &S); void EmitObjCAtThrowStmt(const ObjCAtThrowStmt &S); void EmitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt &S); + void EmitObjCAutoreleasePoolStmt(const ObjCAutoreleasePoolStmt &S); llvm::Constant *getUnwindResumeFn(); llvm::Constant *getUnwindResumeOrRethrowFn(); @@ -1775,6 +1940,12 @@ public: unsigned Alignment, QualType Ty, llvm::MDNode *TBAAInfo = 0); + /// EmitLoadOfScalar - Load a scalar value from an address, taking + /// care to appropriately convert from the memory representation to + /// the LLVM value representation. The l-value must be a simple + /// l-value. + llvm::Value *EmitLoadOfScalar(LValue lvalue); + /// EmitStoreOfScalar - Store a scalar value to an address, taking /// care to appropriately convert from the memory representation to /// the LLVM value representation. @@ -1782,21 +1953,26 @@ public: bool Volatile, unsigned Alignment, QualType Ty, llvm::MDNode *TBAAInfo = 0); + /// EmitStoreOfScalar - Store a scalar value to an address, taking + /// care to appropriately convert from the memory representation to + /// the LLVM value representation. The l-value must be a simple + /// l-value. + void EmitStoreOfScalar(llvm::Value *value, LValue lvalue); + /// EmitLoadOfLValue - Given an expression that represents a value lvalue, /// this method emits the address of the lvalue, then loads the result as an /// rvalue, returning the rvalue. - RValue EmitLoadOfLValue(LValue V, QualType LVType); - RValue EmitLoadOfExtVectorElementLValue(LValue V, QualType LVType); - RValue EmitLoadOfBitfieldLValue(LValue LV, QualType ExprType); + RValue EmitLoadOfLValue(LValue V); + RValue EmitLoadOfExtVectorElementLValue(LValue V); + RValue EmitLoadOfBitfieldLValue(LValue LV); RValue EmitLoadOfPropertyRefLValue(LValue LV, ReturnValueSlot Return = ReturnValueSlot()); /// EmitStoreThroughLValue - Store the specified rvalue into the specified /// lvalue, where both are guaranteed to the have the same type, and that type /// is 'Ty'. - void EmitStoreThroughLValue(RValue Src, LValue Dst, QualType Ty); - void EmitStoreThroughExtVectorComponentLValue(RValue Src, LValue Dst, - QualType Ty); + void EmitStoreThroughLValue(RValue Src, LValue Dst); + void EmitStoreThroughExtVectorComponentLValue(RValue Src, LValue Dst); void EmitStoreThroughPropertyRefLValue(RValue Src, LValue Dst); /// EmitStoreThroughLValue - Store Src into Dst with same constraints as @@ -1805,7 +1981,7 @@ public: /// \param Result [out] - If non-null, this will be set to a Value* for the /// bit-field contents after the store, appropriate for use as the result of /// an assignment to the bit-field. - void EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, QualType Ty, + void EmitStoreThroughBitfieldLValue(RValue Src, LValue Dst, llvm::Value **Result=0); /// Emit an l-value for an assignment (simple or compound) of complex type. @@ -1832,6 +2008,7 @@ public: LValue EmitConditionalOperatorLValue(const AbstractConditionalOperator *E); LValue EmitCastLValue(const CastExpr *E); LValue EmitNullInitializationLValue(const CXXScalarValueInitExpr *E); + LValue EmitMaterializeTemporaryExpr(const MaterializeTemporaryExpr *E); LValue EmitOpaqueValueLValue(const OpaqueValueExpr *e); llvm::Value *EmitIvarOffset(const ObjCInterfaceDecl *Interface, @@ -1897,8 +2074,9 @@ public: ReturnValueSlot ReturnValue = ReturnValueSlot()); llvm::CallSite EmitCallOrInvoke(llvm::Value *Callee, - llvm::Value * const *ArgBegin, - llvm::Value * const *ArgEnd, + llvm::ArrayRef<llvm::Value *> Args, + const llvm::Twine &Name = ""); + llvm::CallSite EmitCallOrInvoke(llvm::Value *Callee, const llvm::Twine &Name = ""); llvm::Value *BuildVirtualCall(const CXXMethodDecl *MD, llvm::Value *This, @@ -1961,6 +2139,59 @@ public: RValue EmitObjCMessageExpr(const ObjCMessageExpr *E, ReturnValueSlot Return = ReturnValueSlot()); + /// Retrieves the default cleanup kind for an ARC cleanup. + /// Except under -fobjc-arc-eh, ARC cleanups are normal-only. + CleanupKind getARCCleanupKind() { + return CGM.getCodeGenOpts().ObjCAutoRefCountExceptions + ? NormalAndEHCleanup : NormalCleanup; + } + + // ARC primitives. + void EmitARCInitWeak(llvm::Value *value, llvm::Value *addr); + void EmitARCDestroyWeak(llvm::Value *addr); + llvm::Value *EmitARCLoadWeak(llvm::Value *addr); + llvm::Value *EmitARCLoadWeakRetained(llvm::Value *addr); + llvm::Value *EmitARCStoreWeak(llvm::Value *value, llvm::Value *addr, + bool ignored); + void EmitARCCopyWeak(llvm::Value *dst, llvm::Value *src); + void EmitARCMoveWeak(llvm::Value *dst, llvm::Value *src); + llvm::Value *EmitARCRetainAutorelease(QualType type, llvm::Value *value); + llvm::Value *EmitARCRetainAutoreleaseNonBlock(llvm::Value *value); + llvm::Value *EmitARCStoreStrong(LValue lvalue, llvm::Value *value, + bool ignored); + llvm::Value *EmitARCStoreStrongCall(llvm::Value *addr, llvm::Value *value, + bool ignored); + llvm::Value *EmitARCRetain(QualType type, llvm::Value *value); + llvm::Value *EmitARCRetainNonBlock(llvm::Value *value); + llvm::Value *EmitARCRetainBlock(llvm::Value *value); + void EmitARCRelease(llvm::Value *value, bool precise); + llvm::Value *EmitARCAutorelease(llvm::Value *value); + llvm::Value *EmitARCAutoreleaseReturnValue(llvm::Value *value); + llvm::Value *EmitARCRetainAutoreleaseReturnValue(llvm::Value *value); + llvm::Value *EmitARCRetainAutoreleasedReturnValue(llvm::Value *value); + + std::pair<LValue,llvm::Value*> + EmitARCStoreAutoreleasing(const BinaryOperator *e); + std::pair<LValue,llvm::Value*> + EmitARCStoreStrong(const BinaryOperator *e, bool ignored); + + llvm::Value *EmitObjCProduceObject(QualType T, llvm::Value *Ptr); + llvm::Value *EmitObjCConsumeObject(QualType T, llvm::Value *Ptr); + llvm::Value *EmitObjCExtendObjectLifetime(QualType T, llvm::Value *Ptr); + + llvm::Value *EmitARCRetainScalarExpr(const Expr *expr); + llvm::Value *EmitARCRetainAutoreleaseScalarExpr(const Expr *expr); + + static Destroyer destroyARCStrongImprecise; + static Destroyer destroyARCStrongPrecise; + static Destroyer destroyARCWeak; + + void EmitObjCAutoreleasePoolPop(llvm::Value *Ptr); + llvm::Value *EmitObjCAutoreleasePoolPush(); + llvm::Value *EmitObjCMRRAutoreleasePoolPush(); + void EmitObjCAutoreleasePoolCleanup(llvm::Value *Ptr); + void EmitObjCMRRAutoreleasePoolPop(llvm::Value *Ptr); + /// EmitReferenceBindingToExpr - Emits a reference binding to the passed in /// expression. Will emit a temporary variable if E is not an LValue. RValue EmitReferenceBindingToExpr(const Expr* E, @@ -2002,6 +2233,10 @@ public: void EmitGCMemmoveCollectable(llvm::Value *DestPtr, llvm::Value *SrcPtr, QualType Ty); + /// EmitExtendGCLifetime - Given a pointer to an Objective-C object, + /// make sure it survives garbage collection until this point. + void EmitExtendGCLifetime(llvm::Value *object); + /// EmitComplexExpr - Emit the computation of the specified expression of /// complex type, returning the result. ComplexPairTy EmitComplexExpr(const Expr *E, @@ -2135,7 +2370,8 @@ private: /// Ty, into individual arguments on the provided vector \arg Args. See /// ABIArgInfo::Expand. void ExpandTypeToArgs(QualType Ty, RValue Src, - llvm::SmallVector<llvm::Value*, 16> &Args); + llvm::SmallVector<llvm::Value*, 16> &Args, + llvm::FunctionType *IRFuncTy); llvm::Value* EmitAsmInput(const AsmStmt &S, const TargetInfo::ConstraintInfo &Info, diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index 7a1a968259f3..0668039a892c 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -62,9 +62,9 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, : Context(C), Features(C.getLangOptions()), CodeGenOpts(CGO), TheModule(M), TheTargetData(TD), TheTargetCodeGenInfo(0), Diags(diags), ABI(createCXXABI(*this)), - Types(C, M, TD, getTargetCodeGenInfo().getABIInfo(), ABI), + Types(C, M, TD, getTargetCodeGenInfo().getABIInfo(), ABI, CGO), TBAA(0), - VTables(*this), Runtime(0), DebugInfo(0), + VTables(*this), Runtime(0), DebugInfo(0), ARCData(0), RRData(0), CFConstantStringClassRef(0), ConstantStringClassRef(0), VMContext(M.getContext()), NSConcreteGlobalBlockDecl(0), NSConcreteStackBlockDecl(0), @@ -88,6 +88,10 @@ CodeGenModule::CodeGenModule(ASTContext &C, const CodeGenOptions &CGO, Block.GlobalUniqueCount = 0; + if (C.getLangOptions().ObjCAutoRefCount) + ARCData = new ARCEntrypoints(); + RRData = new RREntrypoints(); + // Initialize the type cache. llvm::LLVMContext &LLVMContext = M.getContext(); VoidTy = llvm::Type::getVoidTy(LLVMContext); @@ -108,6 +112,8 @@ CodeGenModule::~CodeGenModule() { delete &ABI; delete TBAA; delete DebugInfo; + delete ARCData; + delete RRData; } void CodeGenModule::createObjCRuntime() { @@ -190,6 +196,10 @@ void CodeGenModule::ErrorUnsupported(const Decl *D, const char *Type, getDiags().Report(Context.getFullLoc(D->getLocation()), DiagID) << Msg; } +llvm::ConstantInt *CodeGenModule::getSize(CharUnits size) { + return llvm::ConstantInt::get(SizeTy, size.getQuantity()); +} + void CodeGenModule::setGlobalVisibility(llvm::GlobalValue *GV, const NamedDecl *D) const { // Internal definitions always have default visibility. @@ -347,8 +357,8 @@ void CodeGenModule::EmitCtorList(const CtorList &Fns, const char *GlobalName) { llvm::Type *CtorPFTy = llvm::PointerType::getUnqual(CtorFTy); // Get the type of a ctor entry, { i32, void ()* }. - llvm::StructType* CtorStructTy = - llvm::StructType::get(VMContext, llvm::Type::getInt32Ty(VMContext), + llvm::StructType *CtorStructTy = + llvm::StructType::get(llvm::Type::getInt32Ty(VMContext), llvm::PointerType::getUnqual(CtorFTy), NULL); // Construct the constructor and destructor arrays. @@ -670,7 +680,7 @@ llvm::Constant *CodeGenModule::EmitAnnotateAttr(llvm::GlobalValue *GV, llvm::ConstantExpr::getBitCast(unitGV, SBP), llvm::ConstantInt::get(llvm::Type::getInt32Ty(VMContext), LineNo) }; - return llvm::ConstantStruct::get(VMContext, Fields, 4, false); + return llvm::ConstantStruct::getAnon(Fields); } bool CodeGenModule::MayDeferGeneration(const ValueDecl *Global) { @@ -830,7 +840,8 @@ void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD) { llvm::Constant * CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, const llvm::Type *Ty, - GlobalDecl D, bool ForVTable) { + GlobalDecl D, bool ForVTable, + llvm::Attributes ExtraAttrs) { // Lookup the entry, lazily creating it if necessary. llvm::GlobalValue *Entry = GetGlobalValue(MangledName); if (Entry) { @@ -846,8 +857,7 @@ CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, return Entry; // Make sure the result is of the correct type. - const llvm::Type *PTy = llvm::PointerType::getUnqual(Ty); - return llvm::ConstantExpr::getBitCast(Entry, PTy); + return llvm::ConstantExpr::getBitCast(Entry, Ty->getPointerTo()); } // This function doesn't have a complete type (for example, the return @@ -869,6 +879,8 @@ CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, assert(F->getName() == MangledName && "name was uniqued!"); if (D.getDecl()) SetFunctionAttributes(D, F, IsIncompleteFunction); + if (ExtraAttrs != llvm::Attribute::None) + F->addFnAttr(ExtraAttrs); // This is the first use or definition of a mangled name. If there is a // deferred decl with this name, remember that we need to emit it at the end @@ -915,7 +927,7 @@ CodeGenModule::GetOrCreateLLVMFunction(llvm::StringRef MangledName, return F; } - const llvm::Type *PTy = llvm::PointerType::getUnqual(Ty); + llvm::Type *PTy = llvm::PointerType::getUnqual(Ty); return llvm::ConstantExpr::getBitCast(F, PTy); } @@ -937,8 +949,10 @@ llvm::Constant *CodeGenModule::GetAddrOfFunction(GlobalDecl GD, /// type and name. llvm::Constant * CodeGenModule::CreateRuntimeFunction(const llvm::FunctionType *FTy, - llvm::StringRef Name) { - return GetOrCreateLLVMFunction(Name, FTy, GlobalDecl(), /*ForVTable=*/false); + llvm::StringRef Name, + llvm::Attributes ExtraAttrs) { + return GetOrCreateLLVMFunction(Name, FTy, GlobalDecl(), /*ForVTable=*/false, + ExtraAttrs); } static bool DeclIsConstantGlobal(ASTContext &Context, const VarDecl *D, @@ -1340,7 +1354,8 @@ CodeGenModule::GetLLVMLinkageVarDefinition(const VarDecl *D, ((!CodeGenOpts.NoCommon && !D->getAttr<NoCommonAttr>()) || D->getAttr<CommonAttr>()) && !D->hasExternalStorage() && !D->getInit() && - !D->getAttr<SectionAttr>() && !D->isThreadSpecified()) { + !D->getAttr<SectionAttr>() && !D->isThreadSpecified() && + !D->getAttr<WeakImportAttr>()) { // Thread local vars aren't considered common linkage. return llvm::GlobalVariable::CommonLinkage; } @@ -1398,8 +1413,7 @@ static void ReplaceUsesOfNonProtoTypeWithRealFunction(llvm::GlobalValue *Old, // Okay, we can transform this. Create the new call instruction and copy // over the required information. ArgList.append(CS.arg_begin(), CS.arg_begin() + ArgNo); - llvm::CallInst *NewCall = llvm::CallInst::Create(NewFn, ArgList.begin(), - ArgList.end(), "", CI); + llvm::CallInst *NewCall = llvm::CallInst::Create(NewFn, ArgList, "", CI); ArgList.clear(); if (!NewCall->getType()->isVoidTy()) NewCall->takeName(CI); @@ -1426,7 +1440,7 @@ void CodeGenModule::EmitGlobalFunctionDefinition(GlobalDecl GD) { bool variadic = false; if (const FunctionProtoType *fpt = D->getType()->getAs<FunctionProtoType>()) variadic = fpt->isVariadic(); - const llvm::FunctionType *Ty = getTypes().GetFunctionType(FI, variadic, false); + const llvm::FunctionType *Ty = getTypes().GetFunctionType(FI, variadic); // Get or create the prototype for the function. llvm::Constant *Entry = GetAddrOfFunction(GD, Ty); @@ -1597,10 +1611,10 @@ llvm::Value *CodeGenModule::getBuiltinLibFunction(const FunctionDecl *FD, return GetOrCreateLLVMFunction(Name, Ty, D, /*ForVTable=*/false); } -llvm::Function *CodeGenModule::getIntrinsic(unsigned IID,const llvm::Type **Tys, - unsigned NumTys) { - return llvm::Intrinsic::getDeclaration(&getModule(), - (llvm::Intrinsic::ID)IID, Tys, NumTys); +llvm::Function *CodeGenModule::getIntrinsic(unsigned IID, + llvm::ArrayRef<llvm::Type*> Tys) { + return llvm::Intrinsic::getDeclaration(&getModule(), (llvm::Intrinsic::ID)IID, + Tys); } static llvm::StringMapEntry<llvm::Constant*> & @@ -1997,6 +2011,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { false, true, false, ObjCMethodDecl::Required); D->addInstanceMethod(DTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, DTORMethod, false); + D->setHasCXXStructors(true); } // If the implementation doesn't have any ivar initializers, we don't need @@ -2015,6 +2030,7 @@ void CodeGenModule::EmitObjCIvarInitializations(ObjCImplementationDecl *D) { ObjCMethodDecl::Required); D->addInstanceMethod(CTORMethod); CodeGenFunction(*this).GenerateObjCCtorDtorMethod(D, CTORMethod, true); + D->setHasCXXStructors(true); } /// EmitNamespace - Emit all declarations in a namespace. @@ -2276,7 +2292,7 @@ llvm::Constant *CodeGenModule::getBlockObjectDispose() { } // Otherwise construct the function by hand. - const llvm::Type *args[] = { Int8PtrTy, Int32Ty }; + llvm::Type *args[] = { Int8PtrTy, Int32Ty }; const llvm::FunctionType *fty = llvm::FunctionType::get(VoidTy, args, false); return BlockObjectDispose = @@ -2295,7 +2311,7 @@ llvm::Constant *CodeGenModule::getBlockObjectAssign() { } // Otherwise construct the function by hand. - const llvm::Type *args[] = { Int8PtrTy, Int8PtrTy, Int32Ty }; + llvm::Type *args[] = { Int8PtrTy, Int8PtrTy, Int32Ty }; const llvm::FunctionType *fty = llvm::FunctionType::get(VoidTy, args, false); return BlockObjectAssign = diff --git a/lib/CodeGen/CodeGenModule.h b/lib/CodeGen/CodeGenModule.h index 779a3523b7f6..86fb6d4d030e 100644 --- a/lib/CodeGen/CodeGenModule.h +++ b/lib/CodeGen/CodeGenModule.h @@ -19,10 +19,10 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" +#include "clang/AST/GlobalDecl.h" #include "clang/AST/Mangle.h" #include "CGVTables.h" #include "CodeGenTypes.h" -#include "GlobalDecl.h" #include "llvm/Module.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" @@ -33,6 +33,7 @@ namespace llvm { class Module; class Constant; + class ConstantInt; class Function; class GlobalValue; class TargetData; @@ -98,31 +99,31 @@ namespace CodeGen { struct CodeGenTypeCache { /// void - const llvm::Type *VoidTy; + llvm::Type *VoidTy; /// i8, i32, and i64 - const llvm::IntegerType *Int8Ty, *Int32Ty, *Int64Ty; + llvm::IntegerType *Int8Ty, *Int32Ty, *Int64Ty; /// int - const llvm::IntegerType *IntTy; + llvm::IntegerType *IntTy; /// intptr_t, size_t, and ptrdiff_t, which we assume are the same size. union { - const llvm::IntegerType *IntPtrTy; - const llvm::IntegerType *SizeTy; - const llvm::IntegerType *PtrDiffTy; + llvm::IntegerType *IntPtrTy; + llvm::IntegerType *SizeTy; + llvm::IntegerType *PtrDiffTy; }; /// void* in address space 0 union { - const llvm::PointerType *VoidPtrTy; - const llvm::PointerType *Int8PtrTy; + llvm::PointerType *VoidPtrTy; + llvm::PointerType *Int8PtrTy; }; /// void** in address space 0 union { - const llvm::PointerType *VoidPtrPtrTy; - const llvm::PointerType *Int8PtrPtrTy; + llvm::PointerType *VoidPtrPtrTy; + llvm::PointerType *Int8PtrPtrTy; }; /// The width of a pointer into the generic address space. @@ -131,6 +132,71 @@ namespace CodeGen { /// The alignment of a pointer into the generic address space. unsigned char PointerAlignInBytes; }; + +struct RREntrypoints { + RREntrypoints() { memset(this, 0, sizeof(*this)); } + /// void objc_autoreleasePoolPop(void*); + llvm::Constant *objc_autoreleasePoolPop; + + /// void *objc_autoreleasePoolPush(void); + llvm::Constant *objc_autoreleasePoolPush; +}; + +struct ARCEntrypoints { + ARCEntrypoints() { memset(this, 0, sizeof(*this)); } + + /// id objc_autorelease(id); + llvm::Constant *objc_autorelease; + + /// id objc_autoreleaseReturnValue(id); + llvm::Constant *objc_autoreleaseReturnValue; + + /// void objc_copyWeak(id *dest, id *src); + llvm::Constant *objc_copyWeak; + + /// void objc_destroyWeak(id*); + llvm::Constant *objc_destroyWeak; + + /// id objc_initWeak(id*, id); + llvm::Constant *objc_initWeak; + + /// id objc_loadWeak(id*); + llvm::Constant *objc_loadWeak; + + /// id objc_loadWeakRetained(id*); + llvm::Constant *objc_loadWeakRetained; + + /// void objc_moveWeak(id *dest, id *src); + llvm::Constant *objc_moveWeak; + + /// id objc_retain(id); + llvm::Constant *objc_retain; + + /// id objc_retainAutorelease(id); + llvm::Constant *objc_retainAutorelease; + + /// id objc_retainAutoreleaseReturnValue(id); + llvm::Constant *objc_retainAutoreleaseReturnValue; + + /// id objc_retainAutoreleasedReturnValue(id); + llvm::Constant *objc_retainAutoreleasedReturnValue; + + /// id objc_retainBlock(id); + llvm::Constant *objc_retainBlock; + + /// void objc_release(id); + llvm::Constant *objc_release; + + /// id objc_storeStrong(id*, id); + llvm::Constant *objc_storeStrong; + + /// id objc_storeWeak(id*, id); + llvm::Constant *objc_storeWeak; + + /// A void(void) inline asm to use to mark that the return value of + /// a call will be immediately retain. + llvm::InlineAsm *retainAutoreleasedReturnValueMarker; +}; /// CodeGenModule - This class organizes the cross-function state that is used /// while generating LLVM code. @@ -157,6 +223,8 @@ class CodeGenModule : public CodeGenTypeCache { CGObjCRuntime* Runtime; CGDebugInfo* DebugInfo; + ARCEntrypoints *ARCData; + RREntrypoints *RRData; // WeakRefReferences - A set of references that have only been seen via // a weakref so far. This is used to remove the weak of the reference if we ever @@ -244,8 +312,8 @@ class CodeGenModule : public CodeGenTypeCache { llvm::Constant *BlockObjectAssign; llvm::Constant *BlockObjectDispose; - const llvm::Type *BlockDescriptorType; - const llvm::Type *GenericBlockLiteralType; + llvm::Type *BlockDescriptorType; + llvm::Type *GenericBlockLiteralType; struct { int GlobalUniqueCount; @@ -275,6 +343,16 @@ public: /// getCXXABI() - Return a reference to the configured C++ ABI. CGCXXABI &getCXXABI() { return ABI; } + ARCEntrypoints &getARCEntrypoints() const { + assert(getLangOptions().ObjCAutoRefCount && ARCData != 0); + return *ARCData; + } + + RREntrypoints &getRREntrypoints() const { + assert(RRData != 0); + return *RRData; + } + llvm::Value *getStaticLocalDeclAddress(const VarDecl *VD) { return StaticLocalDeclMap[VD]; } @@ -305,6 +383,9 @@ public: static void DecorateInstruction(llvm::Instruction *Inst, llvm::MDNode *TBAAInfo); + /// getSize - Emit the given number of characters as a value of type size_t. + llvm::ConstantInt *getSize(CharUnits numChars); + /// setGlobalVisibility - Set the visibility for the given LLVM /// GlobalValue. void setGlobalVisibility(llvm::GlobalValue *GV, const NamedDecl *D) const; @@ -422,10 +503,10 @@ public: /// getBlockDescriptorType - Fetches the type of a generic block /// descriptor. - const llvm::Type *getBlockDescriptorType(); + llvm::Type *getBlockDescriptorType(); /// getGenericBlockLiteralType - The type of a generic block literal. - const llvm::Type *getGenericBlockLiteralType(); + llvm::Type *getGenericBlockLiteralType(); /// GetAddrOfGlobalBlock - Gets the address of a block which /// requires no captures. @@ -474,7 +555,7 @@ public: /// created). llvm::Constant *GetAddrOfConstantCString(const std::string &str, const char *GlobalName=0); - + /// GetAddrOfCXXConstructor - Return the address of the constructor of the /// given type. llvm::GlobalValue *GetAddrOfCXXConstructor(const CXXConstructorDecl *ctor, @@ -492,8 +573,8 @@ public: llvm::Value *getBuiltinLibFunction(const FunctionDecl *FD, unsigned BuiltinID); - llvm::Function *getIntrinsic(unsigned IID, const llvm::Type **Tys = 0, - unsigned NumTys = 0); + llvm::Function *getIntrinsic(unsigned IID, llvm::ArrayRef<llvm::Type*> Tys = + llvm::ArrayRef<llvm::Type*>()); /// EmitTopLevelDecl - Emit code for a single top level declaration. void EmitTopLevelDecl(Decl *D); @@ -514,7 +595,9 @@ public: /// CreateRuntimeFunction - Create a new runtime function with the specified /// type and name. llvm::Constant *CreateRuntimeFunction(const llvm::FunctionType *Ty, - llvm::StringRef Name); + llvm::StringRef Name, + llvm::Attributes ExtraAttrs = + llvm::Attribute::None); /// CreateRuntimeVariable - Create a new runtime global variable with the /// specified type and name. llvm::Constant *CreateRuntimeVariable(const llvm::Type *Ty, @@ -642,7 +725,9 @@ private: llvm::Constant *GetOrCreateLLVMFunction(llvm::StringRef MangledName, const llvm::Type *Ty, GlobalDecl D, - bool ForVTable); + bool ForVTable, + llvm::Attributes ExtraAttrs = + llvm::Attribute::None); llvm::Constant *GetOrCreateLLVMGlobal(llvm::StringRef MangledName, const llvm::PointerType *PTy, const VarDecl *D, diff --git a/lib/CodeGen/CodeGenTypes.cpp b/lib/CodeGen/CodeGenTypes.cpp index 8db6fe518684..764688fcb12e 100644 --- a/lib/CodeGen/CodeGenTypes.cpp +++ b/lib/CodeGen/CodeGenTypes.cpp @@ -28,9 +28,10 @@ using namespace CodeGen; CodeGenTypes::CodeGenTypes(ASTContext &Ctx, llvm::Module& M, const llvm::TargetData &TD, const ABIInfo &Info, - CGCXXABI &CXXABI) + CGCXXABI &CXXABI, const CodeGenOptions &CGO) : Context(Ctx), Target(Ctx.Target), TheModule(M), TheTargetData(TD), - TheABIInfo(Info), TheCXXABI(CXXABI) { + TheABIInfo(Info), TheCXXABI(CXXABI), CodeGenOpts(CGO) { + SkippedLayout = false; } CodeGenTypes::~CodeGenTypes() { @@ -44,28 +45,8 @@ CodeGenTypes::~CodeGenTypes() { delete &*I++; } -/// HandleLateResolvedPointers - For top-level ConvertType calls, this handles -/// pointers that are referenced but have not been converted yet. This is used -/// to handle cyclic structures properly. -void CodeGenTypes::HandleLateResolvedPointers() { - assert(!PointersToResolve.empty() && "No pointers to resolve!"); - - // Any pointers that were converted deferred evaluation of their pointee type, - // creating an opaque type instead. This is in order to avoid problems with - // circular types. Loop through all these defered pointees, if any, and - // resolve them now. - while (!PointersToResolve.empty()) { - std::pair<QualType, llvm::OpaqueType*> P = PointersToResolve.pop_back_val(); - - // We can handle bare pointers here because we know that the only pointers - // to the Opaque type are P.second and from other types. Refining the - // opqaue type away will invalidate P.second, but we don't mind :). - const llvm::Type *NT = ConvertTypeForMemRecursive(P.first); - P.second->refineAbstractTypeTo(NT); - } -} - -void CodeGenTypes::addRecordTypeName(const RecordDecl *RD, const llvm::Type *Ty, +void CodeGenTypes::addRecordTypeName(const RecordDecl *RD, + llvm::StructType *Ty, llvm::StringRef suffix) { llvm::SmallString<256> TypeName; llvm::raw_svector_ostream OS(TypeName); @@ -93,47 +74,15 @@ void CodeGenTypes::addRecordTypeName(const RecordDecl *RD, const llvm::Type *Ty, if (!suffix.empty()) OS << suffix; - TheModule.addTypeName(OS.str(), Ty); -} - -/// ConvertType - Convert the specified type to its LLVM form. -const llvm::Type *CodeGenTypes::ConvertType(QualType T, bool IsRecursive) { - const llvm::Type *Result = ConvertTypeRecursive(T); - - // If this is a top-level call to ConvertType and sub-conversions caused - // pointers to get lazily built as opaque types, resolve the pointers, which - // might cause Result to be merged away. - if (!IsRecursive && !PointersToResolve.empty()) { - llvm::PATypeHolder ResultHandle = Result; - HandleLateResolvedPointers(); - Result = ResultHandle; - } - return Result; -} - -const llvm::Type *CodeGenTypes::ConvertTypeRecursive(QualType T) { - T = Context.getCanonicalType(T); - - // See if type is already cached. - llvm::DenseMap<const Type *, llvm::PATypeHolder>::iterator - I = TypeCache.find(T.getTypePtr()); - // If type is found in map and this is not a definition for a opaque - // place holder type then use it. Otherwise, convert type T. - if (I != TypeCache.end()) - return I->second.get(); - - const llvm::Type *ResultType = ConvertNewType(T); - TypeCache.insert(std::make_pair(T.getTypePtr(), - llvm::PATypeHolder(ResultType))); - return ResultType; + Ty->setName(OS.str()); } /// ConvertTypeForMem - Convert type T into a llvm::Type. This differs from /// ConvertType in that it is used to convert to the memory representation for /// a type. For example, the scalar representation for _Bool is i1, but the /// memory representation is usually i8 or i32, depending on the target. -const llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T, bool IsRecursive){ - const llvm::Type *R = ConvertType(T, IsRecursive); +llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T){ + llvm::Type *R = ConvertType(T); // If this is a non-bool type, don't map it. if (!R->isIntegerTy(1)) @@ -142,69 +91,178 @@ const llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T, bool IsRecursive){ // Otherwise, return an integer of the target-specified size. return llvm::IntegerType::get(getLLVMContext(), (unsigned)Context.getTypeSize(T)); +} + +/// isRecordLayoutComplete - Return true if the specified type is already +/// completely laid out. +bool CodeGenTypes::isRecordLayoutComplete(const Type *Ty) const { + llvm::DenseMap<const Type*, llvm::StructType *>::const_iterator I = + RecordDeclTypes.find(Ty); + return I != RecordDeclTypes.end() && !I->second->isOpaque(); } -// Code to verify a given function type is complete, i.e. the return type -// and all of the argument types are complete. -const TagType *CodeGenTypes::VerifyFuncTypeComplete(const Type* T) { - const FunctionType *FT = cast<FunctionType>(T); - if (const TagType* TT = FT->getResultType()->getAs<TagType>()) - if (!TT->getDecl()->isDefinition()) - return TT; - if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(T)) - for (unsigned i = 0; i < FPT->getNumArgs(); i++) - if (const TagType* TT = FPT->getArgType(i)->getAs<TagType>()) - if (!TT->getDecl()->isDefinition()) - return TT; - return 0; +static bool +isSafeToConvert(QualType T, CodeGenTypes &CGT, + llvm::SmallPtrSet<const RecordDecl*, 16> &AlreadyChecked); + + +/// isSafeToConvert - Return true if it is safe to convert the specified record +/// decl to IR and lay it out, false if doing so would cause us to get into a +/// recursive compilation mess. +static bool +isSafeToConvert(const RecordDecl *RD, CodeGenTypes &CGT, + llvm::SmallPtrSet<const RecordDecl*, 16> &AlreadyChecked) { + // If we have already checked this type (maybe the same type is used by-value + // multiple times in multiple structure fields, don't check again. + if (!AlreadyChecked.insert(RD)) return true; + + const Type *Key = CGT.getContext().getTagDeclType(RD).getTypePtr(); + + // If this type is already laid out, converting it is a noop. + if (CGT.isRecordLayoutComplete(Key)) return true; + + // If this type is currently being laid out, we can't recursively compile it. + if (CGT.isRecordBeingLaidOut(Key)) + return false; + + // If this type would require laying out bases that are currently being laid + // out, don't do it. This includes virtual base classes which get laid out + // when a class is translated, even though they aren't embedded by-value into + // the class. + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) { + for (CXXRecordDecl::base_class_const_iterator I = CRD->bases_begin(), + E = CRD->bases_end(); I != E; ++I) + if (!isSafeToConvert(I->getType()->getAs<RecordType>()->getDecl(), + CGT, AlreadyChecked)) + return false; + } + + // If this type would require laying out members that are currently being laid + // out, don't do it. + for (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I != E; ++I) + if (!isSafeToConvert(I->getType(), CGT, AlreadyChecked)) + return false; + + // If there are no problems, lets do it. + return true; +} + +/// isSafeToConvert - Return true if it is safe to convert this field type, +/// which requires the structure elements contained by-value to all be +/// recursively safe to convert. +static bool +isSafeToConvert(QualType T, CodeGenTypes &CGT, + llvm::SmallPtrSet<const RecordDecl*, 16> &AlreadyChecked) { + T = T.getCanonicalType(); + + // If this is a record, check it. + if (const RecordType *RT = dyn_cast<RecordType>(T)) + return isSafeToConvert(RT->getDecl(), CGT, AlreadyChecked); + + // If this is an array, check the elements, which are embedded inline. + if (const ArrayType *AT = dyn_cast<ArrayType>(T)) + return isSafeToConvert(AT->getElementType(), CGT, AlreadyChecked); + + // Otherwise, there is no concern about transforming this. We only care about + // things that are contained by-value in a structure that can have another + // structure as a member. + return true; +} + + +/// isSafeToConvert - Return true if it is safe to convert the specified record +/// decl to IR and lay it out, false if doing so would cause us to get into a +/// recursive compilation mess. +static bool isSafeToConvert(const RecordDecl *RD, CodeGenTypes &CGT) { + // If no structs are being laid out, we can certainly do this one. + if (CGT.noRecordsBeingLaidOut()) return true; + + llvm::SmallPtrSet<const RecordDecl*, 16> AlreadyChecked; + return isSafeToConvert(RD, CGT, AlreadyChecked); +} + + +/// isFuncTypeArgumentConvertible - Return true if the specified type in a +/// function argument or result position can be converted to an IR type at this +/// point. This boils down to being whether it is complete, as well as whether +/// we've temporarily deferred expanding the type because we're in a recursive +/// context. +bool CodeGenTypes::isFuncTypeArgumentConvertible(QualType Ty) { + // If this isn't a tagged type, we can convert it! + const TagType *TT = Ty->getAs<TagType>(); + if (TT == 0) return true; + + + // If it's a tagged type used by-value, but is just a forward decl, we can't + // convert it. Note that getDefinition()==0 is not the same as !isDefinition. + if (TT->getDecl()->getDefinition() == 0) + return false; + + // If this is an enum, then it is always safe to convert. + const RecordType *RT = dyn_cast<RecordType>(TT); + if (RT == 0) return true; + + // Otherwise, we have to be careful. If it is a struct that we're in the + // process of expanding, then we can't convert the function type. That's ok + // though because we must be in a pointer context under the struct, so we can + // just convert it to a dummy type. + // + // We decide this by checking whether ConvertRecordDeclType returns us an + // opaque type for a struct that we know is defined. + return isSafeToConvert(RT->getDecl(), *this); +} + + +/// Code to verify a given function type is complete, i.e. the return type +/// and all of the argument types are complete. Also check to see if we are in +/// a RS_StructPointer context, and if so whether any struct types have been +/// pended. If so, we don't want to ask the ABI lowering code to handle a type +/// that cannot be converted to an IR type. +bool CodeGenTypes::isFuncTypeConvertible(const FunctionType *FT) { + if (!isFuncTypeArgumentConvertible(FT->getResultType())) + return false; + + if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FT)) + for (unsigned i = 0, e = FPT->getNumArgs(); i != e; i++) + if (!isFuncTypeArgumentConvertible(FPT->getArgType(i))) + return false; + + return true; } /// UpdateCompletedType - When we find the full definition for a TagDecl, /// replace the 'opaque' type we previously made for it if applicable. void CodeGenTypes::UpdateCompletedType(const TagDecl *TD) { - const Type *Key = Context.getTagDeclType(TD).getTypePtr(); - llvm::DenseMap<const Type*, llvm::PATypeHolder>::iterator TDTI = - TagDeclTypes.find(Key); - if (TDTI == TagDeclTypes.end()) return; - - // Remember the opaque LLVM type for this tagdecl. - llvm::PATypeHolder OpaqueHolder = TDTI->second; - assert(isa<llvm::OpaqueType>(OpaqueHolder.get()) && - "Updating compilation of an already non-opaque type?"); - - // Remove it from TagDeclTypes so that it will be regenerated. - TagDeclTypes.erase(TDTI); - - // Generate the new type. - const llvm::Type *NT = ConvertTagDeclType(TD); - - // Refine the old opaque type to its new definition. - cast<llvm::OpaqueType>(OpaqueHolder.get())->refineAbstractTypeTo(NT); - - // Since we just completed a tag type, check to see if any function types - // were completed along with the tag type. - // FIXME: This is very inefficient; if we track which function types depend - // on which tag types, though, it should be reasonably efficient. - llvm::DenseMap<const Type*, llvm::PATypeHolder>::iterator i; - for (i = FunctionTypes.begin(); i != FunctionTypes.end(); ++i) { - if (const TagType* TT = VerifyFuncTypeComplete(i->first)) { - // This function type still depends on an incomplete tag type; make sure - // that tag type has an associated opaque type. - ConvertTagDeclType(TT->getDecl()); - } else { - // This function no longer depends on an incomplete tag type; create the - // function type, and refine the opaque type to the new function type. - llvm::PATypeHolder OpaqueHolder = i->second; - const llvm::Type *NFT = ConvertNewType(QualType(i->first, 0)); - cast<llvm::OpaqueType>(OpaqueHolder.get())->refineAbstractTypeTo(NFT); - FunctionTypes.erase(i); + // If this is an enum being completed, then we flush all non-struct types from + // the cache. This allows function types and other things that may be derived + // from the enum to be recomputed. + if (const EnumDecl *ED = dyn_cast<EnumDecl>(TD)) { + // Only flush the cache if we've actually already converted this type. + if (TypeCache.count(ED->getTypeForDecl())) { + // Okay, we formed some types based on this. We speculated that the enum + // would be lowered to i32, so we only need to flush the cache if this + // didn't happen. + if (!ConvertType(ED->getIntegerType())->isIntegerTy(32)) + TypeCache.clear(); } + return; } + + // If we completed a RecordDecl that we previously used and converted to an + // anonymous type, then go ahead and complete it now. + const RecordDecl *RD = cast<RecordDecl>(TD); + if (RD->isDependentType()) return; + + // Only complete it if we converted it already. If we haven't converted it + // yet, we'll just do it lazily. + if (RecordDeclTypes.count(Context.getTagDeclType(RD).getTypePtr())) + ConvertRecordDeclType(RD); } -static const llvm::Type* getTypeForFormat(llvm::LLVMContext &VMContext, - const llvm::fltSemantics &format) { +static llvm::Type *getTypeForFormat(llvm::LLVMContext &VMContext, + const llvm::fltSemantics &format) { if (&format == &llvm::APFloat::IEEEsingle) return llvm::Type::getFloatTy(VMContext); if (&format == &llvm::APFloat::IEEEdouble) @@ -219,10 +277,26 @@ static const llvm::Type* getTypeForFormat(llvm::LLVMContext &VMContext, return 0; } -const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { - const clang::Type &Ty = *Context.getCanonicalType(T).getTypePtr(); +/// ConvertType - Convert the specified type to its LLVM form. +llvm::Type *CodeGenTypes::ConvertType(QualType T) { + T = Context.getCanonicalType(T); + + const Type *Ty = T.getTypePtr(); - switch (Ty.getTypeClass()) { + // RecordTypes are cached and processed specially. + if (const RecordType *RT = dyn_cast<RecordType>(Ty)) + return ConvertRecordDeclType(RT->getDecl()); + + // See if type is already cached. + llvm::DenseMap<const Type *, llvm::Type *>::iterator TCI = TypeCache.find(Ty); + // If type is found in map then use it. Otherwise, convert type T. + if (TCI != TypeCache.end()) + return TCI->second; + + // If we don't have it in the cache, convert it now. + llvm::Type *ResultType = 0; + switch (Ty->getTypeClass()) { + case Type::Record: // Handled above. #define TYPE(Class, Base) #define ABSTRACT_TYPE(Class, Base) #define NON_CANONICAL_TYPE(Class, Base) case Type::Class: @@ -233,18 +307,20 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { break; case Type::Builtin: { - switch (cast<BuiltinType>(Ty).getKind()) { + switch (cast<BuiltinType>(Ty)->getKind()) { case BuiltinType::Void: case BuiltinType::ObjCId: case BuiltinType::ObjCClass: case BuiltinType::ObjCSel: // LLVM void type can only be used as the result of a function call. Just // map to the same as char. - return llvm::Type::getInt8Ty(getLLVMContext()); + ResultType = llvm::Type::getInt8Ty(getLLVMContext()); + break; case BuiltinType::Bool: // Note that we always return bool as i1 for use as a scalar type. - return llvm::Type::getInt1Ty(getLLVMContext()); + ResultType = llvm::Type::getInt1Ty(getLLVMContext()); + break; case BuiltinType::Char_S: case BuiltinType::Char_U: @@ -262,24 +338,26 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { case BuiltinType::WChar_U: case BuiltinType::Char16: case BuiltinType::Char32: - return llvm::IntegerType::get(getLLVMContext(), - static_cast<unsigned>(Context.getTypeSize(T))); + ResultType = llvm::IntegerType::get(getLLVMContext(), + static_cast<unsigned>(Context.getTypeSize(T))); + break; case BuiltinType::Float: case BuiltinType::Double: case BuiltinType::LongDouble: - return getTypeForFormat(getLLVMContext(), - Context.getFloatTypeSemantics(T)); + ResultType = getTypeForFormat(getLLVMContext(), + Context.getFloatTypeSemantics(T)); + break; - case BuiltinType::NullPtr: { + case BuiltinType::NullPtr: // Model std::nullptr_t as i8* - const llvm::Type *Ty = llvm::Type::getInt8Ty(getLLVMContext()); - return llvm::PointerType::getUnqual(Ty); - } + ResultType = llvm::Type::getInt8PtrTy(getLLVMContext()); + break; case BuiltinType::UInt128: case BuiltinType::Int128: - return llvm::IntegerType::get(getLLVMContext(), 128); + ResultType = llvm::IntegerType::get(getLLVMContext(), 128); + break; case BuiltinType::Overload: case BuiltinType::Dependent: @@ -288,107 +366,145 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { llvm_unreachable("Unexpected placeholder builtin type!"); break; } - llvm_unreachable("Unknown builtin type!"); break; } case Type::Complex: { - const llvm::Type *EltTy = - ConvertTypeRecursive(cast<ComplexType>(Ty).getElementType()); - return llvm::StructType::get(TheModule.getContext(), EltTy, EltTy, NULL); + llvm::Type *EltTy = ConvertType(cast<ComplexType>(Ty)->getElementType()); + ResultType = llvm::StructType::get(EltTy, EltTy, NULL); + break; } case Type::LValueReference: case Type::RValueReference: { - const ReferenceType &RTy = cast<ReferenceType>(Ty); - QualType ETy = RTy.getPointeeType(); - llvm::OpaqueType *PointeeType = llvm::OpaqueType::get(getLLVMContext()); - PointersToResolve.push_back(std::make_pair(ETy, PointeeType)); + const ReferenceType *RTy = cast<ReferenceType>(Ty); + QualType ETy = RTy->getPointeeType(); + llvm::Type *PointeeType = ConvertTypeForMem(ETy); unsigned AS = Context.getTargetAddressSpace(ETy); - return llvm::PointerType::get(PointeeType, AS); + ResultType = llvm::PointerType::get(PointeeType, AS); + break; } case Type::Pointer: { - const PointerType &PTy = cast<PointerType>(Ty); - QualType ETy = PTy.getPointeeType(); - llvm::OpaqueType *PointeeType = llvm::OpaqueType::get(getLLVMContext()); - PointersToResolve.push_back(std::make_pair(ETy, PointeeType)); + const PointerType *PTy = cast<PointerType>(Ty); + QualType ETy = PTy->getPointeeType(); + llvm::Type *PointeeType = ConvertTypeForMem(ETy); + if (PointeeType->isVoidTy()) + PointeeType = llvm::Type::getInt8Ty(getLLVMContext()); unsigned AS = Context.getTargetAddressSpace(ETy); - return llvm::PointerType::get(PointeeType, AS); + ResultType = llvm::PointerType::get(PointeeType, AS); + break; } case Type::VariableArray: { - const VariableArrayType &A = cast<VariableArrayType>(Ty); - assert(A.getIndexTypeCVRQualifiers() == 0 && + const VariableArrayType *A = cast<VariableArrayType>(Ty); + assert(A->getIndexTypeCVRQualifiers() == 0 && "FIXME: We only handle trivial array types so far!"); // VLAs resolve to the innermost element type; this matches // the return of alloca, and there isn't any obviously better choice. - return ConvertTypeForMemRecursive(A.getElementType()); + ResultType = ConvertTypeForMem(A->getElementType()); + break; } case Type::IncompleteArray: { - const IncompleteArrayType &A = cast<IncompleteArrayType>(Ty); - assert(A.getIndexTypeCVRQualifiers() == 0 && + const IncompleteArrayType *A = cast<IncompleteArrayType>(Ty); + assert(A->getIndexTypeCVRQualifiers() == 0 && "FIXME: We only handle trivial array types so far!"); - // int X[] -> [0 x int] - return llvm::ArrayType::get(ConvertTypeForMemRecursive(A.getElementType()), - 0); + // int X[] -> [0 x int], unless the element type is not sized. If it is + // unsized (e.g. an incomplete struct) just use [0 x i8]. + ResultType = ConvertTypeForMem(A->getElementType()); + if (!ResultType->isSized()) { + SkippedLayout = true; + ResultType = llvm::Type::getInt8Ty(getLLVMContext()); + } + ResultType = llvm::ArrayType::get(ResultType, 0); + break; } case Type::ConstantArray: { - const ConstantArrayType &A = cast<ConstantArrayType>(Ty); - const llvm::Type *EltTy = ConvertTypeForMemRecursive(A.getElementType()); - return llvm::ArrayType::get(EltTy, A.getSize().getZExtValue()); + const ConstantArrayType *A = cast<ConstantArrayType>(Ty); + const llvm::Type *EltTy = ConvertTypeForMem(A->getElementType()); + ResultType = llvm::ArrayType::get(EltTy, A->getSize().getZExtValue()); + break; } case Type::ExtVector: case Type::Vector: { - const VectorType &VT = cast<VectorType>(Ty); - return llvm::VectorType::get(ConvertTypeRecursive(VT.getElementType()), - VT.getNumElements()); + const VectorType *VT = cast<VectorType>(Ty); + ResultType = llvm::VectorType::get(ConvertType(VT->getElementType()), + VT->getNumElements()); + break; } case Type::FunctionNoProto: case Type::FunctionProto: { + const FunctionType *FT = cast<FunctionType>(Ty); // First, check whether we can build the full function type. If the // function type depends on an incomplete type (e.g. a struct or enum), we - // cannot lower the function type. Instead, turn it into an Opaque pointer - // and have UpdateCompletedType revisit the function type when/if the opaque - // argument type is defined. - if (const TagType *TT = VerifyFuncTypeComplete(&Ty)) { - // This function's type depends on an incomplete tag type; make sure - // we have an opaque type corresponding to the tag type. - ConvertTagDeclType(TT->getDecl()); - // Create an opaque type for this function type, save it, and return it. - llvm::Type *ResultType = llvm::OpaqueType::get(getLLVMContext()); - FunctionTypes.insert(std::make_pair(&Ty, ResultType)); - return ResultType; + // cannot lower the function type. + if (!isFuncTypeConvertible(FT)) { + // This function's type depends on an incomplete tag type. + // Return a placeholder type. + ResultType = llvm::StructType::get(getLLVMContext()); + + SkippedLayout = true; + break; + } + + // While we're converting the argument types for a function, we don't want + // to recursively convert any pointed-to structs. Converting directly-used + // structs is ok though. + if (!RecordsBeingLaidOut.insert(Ty)) { + ResultType = llvm::StructType::get(getLLVMContext()); + + SkippedLayout = true; + break; } // The function type can be built; call the appropriate routines to // build it. const CGFunctionInfo *FI; bool isVariadic; - if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(&Ty)) { + if (const FunctionProtoType *FPT = dyn_cast<FunctionProtoType>(FT)) { FI = &getFunctionInfo( - CanQual<FunctionProtoType>::CreateUnsafe(QualType(FPT, 0)), - true /*Recursive*/); + CanQual<FunctionProtoType>::CreateUnsafe(QualType(FPT, 0))); isVariadic = FPT->isVariadic(); } else { - const FunctionNoProtoType *FNPT = cast<FunctionNoProtoType>(&Ty); + const FunctionNoProtoType *FNPT = cast<FunctionNoProtoType>(FT); FI = &getFunctionInfo( - CanQual<FunctionNoProtoType>::CreateUnsafe(QualType(FNPT, 0)), - true /*Recursive*/); + CanQual<FunctionNoProtoType>::CreateUnsafe(QualType(FNPT, 0))); isVariadic = true; } + + // If there is something higher level prodding our CGFunctionInfo, then + // don't recurse into it again. + if (FunctionsBeingProcessed.count(FI)) { + + ResultType = llvm::StructType::get(getLLVMContext()); + SkippedLayout = true; + } else { - return GetFunctionType(*FI, isVariadic, true); + // Otherwise, we're good to go, go ahead and convert it. + ResultType = GetFunctionType(*FI, isVariadic); + } + + RecordsBeingLaidOut.erase(Ty); + + if (SkippedLayout) + TypeCache.clear(); + + if (RecordsBeingLaidOut.empty()) + while (!DeferredRecords.empty()) + ConvertRecordDeclType(DeferredRecords.pop_back_val()); + break; } case Type::ObjCObject: - return ConvertTypeRecursive(cast<ObjCObjectType>(Ty).getBaseType()); + ResultType = ConvertType(cast<ObjCObjectType>(Ty)->getBaseType()); + break; case Type::ObjCInterface: { // Objective-C interfaces are always opaque (outside of the // runtime, which can do whatever it likes); we never refine // these. - const llvm::Type *&T = InterfaceTypes[cast<ObjCInterfaceType>(&Ty)]; + llvm::Type *&T = InterfaceTypes[cast<ObjCInterfaceType>(Ty)]; if (!T) - T = llvm::OpaqueType::get(getLLVMContext()); - return T; + T = llvm::StructType::createNamed(getLLVMContext(), ""); + ResultType = T; + break; } case Type::ObjCObjectPointer: { @@ -396,103 +512,105 @@ const llvm::Type *CodeGenTypes::ConvertNewType(QualType T) { // pointer to the underlying interface type. We don't need to worry about // recursive conversion. const llvm::Type *T = - ConvertTypeRecursive(cast<ObjCObjectPointerType>(Ty).getPointeeType()); - return llvm::PointerType::getUnqual(T); + ConvertType(cast<ObjCObjectPointerType>(Ty)->getPointeeType()); + ResultType = T->getPointerTo(); + break; } - case Type::Record: case Type::Enum: { - const TagDecl *TD = cast<TagType>(Ty).getDecl(); - const llvm::Type *Res = ConvertTagDeclType(TD); - - if (const RecordDecl *RD = dyn_cast<RecordDecl>(TD)) - addRecordTypeName(RD, Res, llvm::StringRef()); - return Res; + const EnumDecl *ED = cast<EnumType>(Ty)->getDecl(); + if (ED->isDefinition() || ED->isFixed()) + return ConvertType(ED->getIntegerType()); + // Return a placeholder 'i32' type. This can be changed later when the + // type is defined (see UpdateCompletedType), but is likely to be the + // "right" answer. + ResultType = llvm::Type::getInt32Ty(getLLVMContext()); + break; } case Type::BlockPointer: { - const QualType FTy = cast<BlockPointerType>(Ty).getPointeeType(); - llvm::OpaqueType *PointeeType = llvm::OpaqueType::get(getLLVMContext()); - PointersToResolve.push_back(std::make_pair(FTy, PointeeType)); + const QualType FTy = cast<BlockPointerType>(Ty)->getPointeeType(); + llvm::Type *PointeeType = ConvertTypeForMem(FTy); unsigned AS = Context.getTargetAddressSpace(FTy); - return llvm::PointerType::get(PointeeType, AS); + ResultType = llvm::PointerType::get(PointeeType, AS); + break; } case Type::MemberPointer: { - return getCXXABI().ConvertMemberPointerType(cast<MemberPointerType>(&Ty)); + ResultType = + getCXXABI().ConvertMemberPointerType(cast<MemberPointerType>(Ty)); + break; } } - - // FIXME: implement. - return llvm::OpaqueType::get(getLLVMContext()); + + assert(ResultType && "Didn't convert a type?"); + + TypeCache[Ty] = ResultType; + return ResultType; } -/// ConvertTagDeclType - Lay out a tagged decl type like struct or union or -/// enum. -const llvm::Type *CodeGenTypes::ConvertTagDeclType(const TagDecl *TD) { +/// ConvertRecordDeclType - Lay out a tagged decl type like struct or union. +llvm::StructType *CodeGenTypes::ConvertRecordDeclType(const RecordDecl *RD) { // TagDecl's are not necessarily unique, instead use the (clang) // type connected to the decl. - const Type *Key = - Context.getTagDeclType(TD).getTypePtr(); - llvm::DenseMap<const Type*, llvm::PATypeHolder>::iterator TDTI = - TagDeclTypes.find(Key); - - // If we've already compiled this tag type, use the previous definition. - if (TDTI != TagDeclTypes.end()) - return TDTI->second; - - const EnumDecl *ED = dyn_cast<EnumDecl>(TD); - - // If this is still a forward declaration, just define an opaque - // type to use for this tagged decl. - // C++0x: If this is a enumeration type with fixed underlying type, - // consider it complete. - if (!TD->isDefinition() && !(ED && ED->isFixed())) { - llvm::Type *ResultType = llvm::OpaqueType::get(getLLVMContext()); - TagDeclTypes.insert(std::make_pair(Key, ResultType)); - return ResultType; - } - - // Okay, this is a definition of a type. Compile the implementation now. - - if (ED) // Don't bother storing enums in TagDeclTypes. - return ConvertTypeRecursive(ED->getIntegerType()); + const Type *Key = Context.getTagDeclType(RD).getTypePtr(); - // This decl could well be recursive. In this case, insert an opaque - // definition of this type, which the recursive uses will get. We will then - // refine this opaque version later. + llvm::StructType *&Entry = RecordDeclTypes[Key]; - // Create new OpaqueType now for later use in case this is a recursive - // type. This will later be refined to the actual type. - llvm::PATypeHolder ResultHolder = llvm::OpaqueType::get(getLLVMContext()); - TagDeclTypes.insert(std::make_pair(Key, ResultHolder)); + // If we don't have a StructType at all yet, create the forward declaration. + if (Entry == 0) { + Entry = llvm::StructType::createNamed(getLLVMContext(), ""); + addRecordTypeName(RD, Entry, ""); + } + llvm::StructType *Ty = Entry; - const RecordDecl *RD = cast<const RecordDecl>(TD); + // If this is still a forward declaration, or the LLVM type is already + // complete, there's nothing more to do. + RD = RD->getDefinition(); + if (RD == 0 || !Ty->isOpaque()) + return Ty; + + // If converting this type would cause us to infinitely loop, don't do it! + if (!isSafeToConvert(RD, *this)) { + DeferredRecords.push_back(RD); + return Ty; + } + // Okay, this is a definition of a type. Compile the implementation now. + bool InsertResult = RecordsBeingLaidOut.insert(Key); (void)InsertResult; + assert(InsertResult && "Recursively compiling a struct?"); + // Force conversion of non-virtual base classes recursively. - if (const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(TD)) { - for (CXXRecordDecl::base_class_const_iterator i = RD->bases_begin(), - e = RD->bases_end(); i != e; ++i) { - if (!i->isVirtual()) { - const CXXRecordDecl *Base = - cast<CXXRecordDecl>(i->getType()->getAs<RecordType>()->getDecl()); - ConvertTagDeclType(Base); - } + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) { + for (CXXRecordDecl::base_class_const_iterator i = CRD->bases_begin(), + e = CRD->bases_end(); i != e; ++i) { + if (i->isVirtual()) continue; + + ConvertRecordDeclType(i->getType()->getAs<RecordType>()->getDecl()); } } // Layout fields. - CGRecordLayout *Layout = ComputeRecordLayout(RD); - + CGRecordLayout *Layout = ComputeRecordLayout(RD, Ty); CGRecordLayouts[Key] = Layout; - const llvm::Type *ResultType = Layout->getLLVMType(); - // Refine our Opaque type to ResultType. This can invalidate ResultType, so - // make sure to read the result out of the holder. - cast<llvm::OpaqueType>(ResultHolder.get()) - ->refineAbstractTypeTo(ResultType); + // We're done laying out this struct. + bool EraseResult = RecordsBeingLaidOut.erase(Key); (void)EraseResult; + assert(EraseResult && "struct not in RecordsBeingLaidOut set?"); + + // If this struct blocked a FunctionType conversion, then recompute whatever + // was derived from that. + // FIXME: This is hugely overconservative. + if (SkippedLayout) + TypeCache.clear(); + + // If we're done converting the outer-most record, then convert any deferred + // structs as well. + if (RecordsBeingLaidOut.empty()) + while (!DeferredRecords.empty()) + ConvertRecordDeclType(DeferredRecords.pop_back_val()); - return ResultHolder.get(); + return Ty; } /// getCGRecordLayout - Return record layout info for the given record decl. @@ -503,7 +621,7 @@ CodeGenTypes::getCGRecordLayout(const RecordDecl *RD) { const CGRecordLayout *Layout = CGRecordLayouts.lookup(Key); if (!Layout) { // Compute the type information. - ConvertTagDeclType(RD); + ConvertRecordDeclType(RD); // Now try again. Layout = CGRecordLayouts.lookup(Key); @@ -513,15 +631,6 @@ CodeGenTypes::getCGRecordLayout(const RecordDecl *RD) { return *Layout; } -void CodeGenTypes::addBaseSubobjectTypeName(const CXXRecordDecl *RD, - const CGRecordLayout &layout) { - llvm::StringRef suffix; - if (layout.getBaseSubobjectLLVMType() != layout.getLLVMType()) - suffix = ".base"; - - addRecordTypeName(RD, layout.getBaseSubobjectLLVMType(), suffix); -} - bool CodeGenTypes::isZeroInitializable(QualType T) { // No need to check for member pointers when not compiling C++. if (!Context.getLangOptions().CPlusPlus) diff --git a/lib/CodeGen/CodeGenTypes.h b/lib/CodeGen/CodeGenTypes.h index ff1eb4c45b59..7c0fb8164373 100644 --- a/lib/CodeGen/CodeGenTypes.h +++ b/lib/CodeGen/CodeGenTypes.h @@ -15,7 +15,7 @@ #define CLANG_CODEGEN_CODEGENTYPES_H #include "CGCall.h" -#include "GlobalDecl.h" +#include "clang/AST/GlobalDecl.h" #include "llvm/Module.h" #include "llvm/ADT/DenseMap.h" #include <vector> @@ -23,11 +23,10 @@ namespace llvm { class FunctionType; class Module; - class OpaqueType; - class PATypeHolder; class TargetData; class Type; class LLVMContext; + class StructType; } namespace clang { @@ -37,6 +36,7 @@ namespace clang { class CXXConstructorDecl; class CXXDestructorDecl; class CXXMethodDecl; + class CodeGenOptions; class FieldDecl; class FunctionProtoType; class ObjCInterfaceDecl; @@ -58,91 +58,83 @@ namespace CodeGen { class CodeGenTypes { ASTContext &Context; const TargetInfo &Target; - llvm::Module& TheModule; - const llvm::TargetData& TheTargetData; - const ABIInfo& TheABIInfo; + llvm::Module &TheModule; + const llvm::TargetData &TheTargetData; + const ABIInfo &TheABIInfo; CGCXXABI &TheCXXABI; - - llvm::SmallVector<std::pair<QualType, - llvm::OpaqueType *>, 8> PointersToResolve; - - llvm::DenseMap<const Type*, llvm::PATypeHolder> TagDeclTypes; - - llvm::DenseMap<const Type*, llvm::PATypeHolder> FunctionTypes; + const CodeGenOptions &CodeGenOpts; /// The opaque type map for Objective-C interfaces. All direct /// manipulation is done by the runtime interfaces, which are /// responsible for coercing to the appropriate type; these opaque /// types are never refined. - llvm::DenseMap<const ObjCInterfaceType*, const llvm::Type *> InterfaceTypes; + llvm::DenseMap<const ObjCInterfaceType*, llvm::Type *> InterfaceTypes; /// CGRecordLayouts - This maps llvm struct type with corresponding /// record layout info. llvm::DenseMap<const Type*, CGRecordLayout *> CGRecordLayouts; + /// RecordDeclTypes - This contains the LLVM IR type for any converted + /// RecordDecl. + llvm::DenseMap<const Type*, llvm::StructType *> RecordDeclTypes; + /// FunctionInfos - Hold memoized CGFunctionInfo results. llvm::FoldingSet<CGFunctionInfo> FunctionInfos; + /// RecordsBeingLaidOut - This set keeps track of records that we're currently + /// converting to an IR type. For example, when converting: + /// struct A { struct B { int x; } } when processing 'x', the 'A' and 'B' + /// types will be in this set. + llvm::SmallPtrSet<const Type*, 4> RecordsBeingLaidOut; + + llvm::SmallPtrSet<const CGFunctionInfo*, 4> FunctionsBeingProcessed; + + /// SkippedLayout - True if we didn't layout a function due to a being inside + /// a recursive struct conversion, set this to true. + bool SkippedLayout; + + llvm::SmallVector<const RecordDecl *, 8> DeferredRecords; + private: - /// TypeCache - This map keeps cache of llvm::Types (through PATypeHolder) - /// and maps llvm::Types to corresponding clang::Type. llvm::PATypeHolder is - /// used instead of llvm::Type because it allows us to bypass potential - /// dangling type pointers due to type refinement on llvm side. - llvm::DenseMap<const Type *, llvm::PATypeHolder> TypeCache; - - /// ConvertNewType - Convert type T into a llvm::Type. Do not use this - /// method directly because it does not do any type caching. This method - /// is available only for ConvertType(). CovertType() is preferred - /// interface to convert type T into a llvm::Type. - const llvm::Type *ConvertNewType(QualType T); - - /// HandleLateResolvedPointers - For top-level ConvertType calls, this handles - /// pointers that are referenced but have not been converted yet. This is - /// used to handle cyclic structures properly. - void HandleLateResolvedPointers(); - - /// addRecordTypeName - Compute a name from the given record decl with an - /// optional suffix and name the given LLVM type using it. - void addRecordTypeName(const RecordDecl *RD, const llvm::Type *Ty, - llvm::StringRef suffix); + /// TypeCache - This map keeps cache of llvm::Types + /// and maps llvm::Types to corresponding clang::Type. + llvm::DenseMap<const Type *, llvm::Type *> TypeCache; public: CodeGenTypes(ASTContext &Ctx, llvm::Module &M, const llvm::TargetData &TD, - const ABIInfo &Info, CGCXXABI &CXXABI); + const ABIInfo &Info, CGCXXABI &CXXABI, + const CodeGenOptions &Opts); ~CodeGenTypes(); const llvm::TargetData &getTargetData() const { return TheTargetData; } const TargetInfo &getTarget() const { return Target; } ASTContext &getContext() const { return Context; } const ABIInfo &getABIInfo() const { return TheABIInfo; } + const CodeGenOptions &getCodeGenOpts() const { return CodeGenOpts; } CGCXXABI &getCXXABI() const { return TheCXXABI; } llvm::LLVMContext &getLLVMContext() { return TheModule.getContext(); } /// ConvertType - Convert type T into a llvm::Type. - const llvm::Type *ConvertType(QualType T, bool IsRecursive = false); - const llvm::Type *ConvertTypeRecursive(QualType T); + llvm::Type *ConvertType(QualType T); /// ConvertTypeForMem - Convert type T into a llvm::Type. This differs from /// ConvertType in that it is used to convert to the memory representation for /// a type. For example, the scalar representation for _Bool is i1, but the /// memory representation is usually i8 or i32, depending on the target. - const llvm::Type *ConvertTypeForMem(QualType T, bool IsRecursive = false); - const llvm::Type *ConvertTypeForMemRecursive(QualType T) { - return ConvertTypeForMem(T, true); - } + llvm::Type *ConvertTypeForMem(QualType T); /// GetFunctionType - Get the LLVM function type for \arg Info. - const llvm::FunctionType *GetFunctionType(const CGFunctionInfo &Info, - bool IsVariadic, - bool IsRecursive = false); + llvm::FunctionType *GetFunctionType(const CGFunctionInfo &Info, + bool IsVariadic); - const llvm::FunctionType *GetFunctionType(GlobalDecl GD); + llvm::FunctionType *GetFunctionType(GlobalDecl GD); - /// VerifyFuncTypeComplete - Utility to check whether a function type can + /// isFuncTypeConvertible - Utility to check whether a function type can /// be converted to an LLVM type (i.e. doesn't depend on an incomplete tag /// type). - static const TagType *VerifyFuncTypeComplete(const Type* T); - + bool isFuncTypeConvertible(const FunctionType *FT); + bool isFuncTypeArgumentConvertible(QualType Ty); + /// GetFunctionTypeForVTable - Get the LLVM function type for use in a vtable, /// given a CXXMethodDecl. If the method to has an incomplete return type, /// and/or incomplete argument types, this will return the opaque type. @@ -150,11 +142,6 @@ public: const CGRecordLayout &getCGRecordLayout(const RecordDecl*); - /// addBaseSubobjectTypeName - Add a type name for the base subobject of the - /// given record layout. - void addBaseSubobjectTypeName(const CXXRecordDecl *RD, - const CGRecordLayout &layout); - /// UpdateCompletedType - When we find the full definition for a TagDecl, /// replace the 'opaque' type we previously made for it if applicable. void UpdateCompletedType(const TagDecl *TD); @@ -180,10 +167,8 @@ public: Ty->getExtInfo()); } - const CGFunctionInfo &getFunctionInfo(CanQual<FunctionProtoType> Ty, - bool IsRecursive = false); - const CGFunctionInfo &getFunctionInfo(CanQual<FunctionNoProtoType> Ty, - bool IsRecursive = false); + const CGFunctionInfo &getFunctionInfo(CanQual<FunctionProtoType> Ty); + const CGFunctionInfo &getFunctionInfo(CanQual<FunctionNoProtoType> Ty); /// getFunctionInfo - Get the function info for a member function of /// the given type. This is used for calls through member function @@ -206,23 +191,27 @@ public: /// \param ArgTys - must all actually be canonical as params const CGFunctionInfo &getFunctionInfo(CanQualType RetTy, const llvm::SmallVectorImpl<CanQualType> &ArgTys, - const FunctionType::ExtInfo &Info, - bool IsRecursive = false); + const FunctionType::ExtInfo &Info); /// \brief Compute a new LLVM record layout object for the given record. - CGRecordLayout *ComputeRecordLayout(const RecordDecl *D); + CGRecordLayout *ComputeRecordLayout(const RecordDecl *D, + llvm::StructType *Ty); + + /// addRecordTypeName - Compute a name from the given record decl with an + /// optional suffix and name the given LLVM type using it. + void addRecordTypeName(const RecordDecl *RD, llvm::StructType *Ty, + llvm::StringRef suffix); + public: // These are internal details of CGT that shouldn't be used externally. - /// ConvertTagDeclType - Lay out a tagged decl type like struct or union or - /// enum. - const llvm::Type *ConvertTagDeclType(const TagDecl *TD); + /// ConvertRecordDeclType - Lay out a tagged decl type like struct or union. + llvm::StructType *ConvertRecordDeclType(const RecordDecl *TD); /// GetExpandedTypes - Expand the type \arg Ty into the LLVM /// argument types it would be passed as on the provided vector \arg /// ArgTys. See ABIArgInfo::Expand. void GetExpandedTypes(QualType type, - llvm::SmallVectorImpl<const llvm::Type*> &expanded, - bool isRecursive); + llvm::SmallVectorImpl<llvm::Type*> &expanded); /// IsZeroInitializable - Return whether a type can be /// zero-initialized (in the C++ sense) with an LLVM zeroinitializer. @@ -231,6 +220,15 @@ public: // These are internal details of CGT that shouldn't be used externally. /// IsZeroInitializable - Return whether a record type can be /// zero-initialized (in the C++ sense) with an LLVM zeroinitializer. bool isZeroInitializable(const CXXRecordDecl *RD); + + bool isRecordLayoutComplete(const Type *Ty) const; + bool noRecordsBeingLaidOut() const { + return RecordsBeingLaidOut.empty(); + } + bool isRecordBeingLaidOut(const Type *Ty) const { + return RecordsBeingLaidOut.count(Ty); + } + }; } // end namespace CodeGen diff --git a/lib/CodeGen/ItaniumCXXABI.cpp b/lib/CodeGen/ItaniumCXXABI.cpp index 12ef9bd030bd..0c86080fa80e 100644 --- a/lib/CodeGen/ItaniumCXXABI.cpp +++ b/lib/CodeGen/ItaniumCXXABI.cpp @@ -24,6 +24,7 @@ #include "CodeGenModule.h" #include <clang/AST/Mangle.h> #include <clang/AST/Type.h> +#include <llvm/Intrinsics.h> #include <llvm/Target/TargetData.h> #include <llvm/Value.h> @@ -33,15 +34,15 @@ using namespace CodeGen; namespace { class ItaniumCXXABI : public CodeGen::CGCXXABI { private: - const llvm::IntegerType *PtrDiffTy; + llvm::IntegerType *PtrDiffTy; protected: bool IsARM; // It's a little silly for us to cache this. - const llvm::IntegerType *getPtrDiffTy() { + llvm::IntegerType *getPtrDiffTy() { if (!PtrDiffTy) { QualType T = getContext().getPointerDiffType(); - const llvm::Type *Ty = CGM.getTypes().ConvertTypeRecursive(T); + llvm::Type *Ty = CGM.getTypes().ConvertType(T); PtrDiffTy = cast<llvm::IntegerType>(Ty); } return PtrDiffTy; @@ -57,7 +58,7 @@ public: bool isZeroInitializable(const MemberPointerType *MPT); - const llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); + llvm::Type *ConvertMemberPointerType(const MemberPointerType *MPT); llvm::Value *EmitLoadOfMemberFunctionPointer(CodeGenFunction &CGF, llvm::Value *&This, @@ -175,13 +176,11 @@ CodeGen::CGCXXABI *CodeGen::CreateARMCXXABI(CodeGenModule &CGM) { return new ARMCXXABI(CGM); } -const llvm::Type * +llvm::Type * ItaniumCXXABI::ConvertMemberPointerType(const MemberPointerType *MPT) { if (MPT->isMemberDataPointer()) return getPtrDiffTy(); - else - return llvm::StructType::get(CGM.getLLVMContext(), - getPtrDiffTy(), getPtrDiffTy(), NULL); + return llvm::StructType::get(getPtrDiffTy(), getPtrDiffTy(), NULL); } /// In the Itanium and ARM ABIs, method pointers have the form: @@ -473,8 +472,7 @@ ItaniumCXXABI::EmitMemberPointerConversion(llvm::Constant *C, else Values[1] = llvm::ConstantExpr::getAdd(CS->getOperand(1), Offset); - return llvm::ConstantStruct::get(CGM.getLLVMContext(), Values, 2, - /*Packed=*/false); + return llvm::ConstantStruct::get(CS->getType(), Values); } @@ -489,8 +487,7 @@ ItaniumCXXABI::EmitNullMemberPointer(const MemberPointerType *MPT) { llvm::Constant *Zero = llvm::ConstantInt::get(ptrdiff_t, 0); llvm::Constant *Values[2] = { Zero, Zero }; - return llvm::ConstantStruct::get(CGM.getLLVMContext(), Values, 2, - /*Packed=*/false); + return llvm::ConstantStruct::getAnon(Values); } llvm::Constant * @@ -540,7 +537,7 @@ llvm::Constant *ItaniumCXXABI::EmitMemberPointer(const CXXMethodDecl *MD) { const FunctionProtoType *FPT = MD->getType()->castAs<FunctionProtoType>(); const llvm::Type *Ty; // Check whether the function has a computable LLVM signature. - if (!CodeGenTypes::VerifyFuncTypeComplete(FPT)) { + if (Types.isFuncTypeConvertible(FPT)) { // The function has a computable LLVM signature; use the correct type. Ty = Types.GetFunctionType(Types.getFunctionInfo(MD), FPT->isVariadic()); @@ -555,8 +552,7 @@ llvm::Constant *ItaniumCXXABI::EmitMemberPointer(const CXXMethodDecl *MD) { MemPtr[1] = llvm::ConstantInt::get(ptrdiff_t, 0); } - return llvm::ConstantStruct::get(CGM.getLLVMContext(), - MemPtr, 2, /*Packed=*/false); + return llvm::ConstantStruct::getAnon(MemPtr); } /// The comparison algorithm is pretty easy: the member pointers are @@ -802,10 +798,27 @@ bool ItaniumCXXABI::NeedsArrayCookie(const CXXNewExpr *expr) { if (expr->doesUsualArrayDeleteWantSize()) return true; + // Automatic Reference Counting: + // We need an array cookie for pointers with strong or weak lifetime. + QualType AllocatedType = expr->getAllocatedType(); + if (getContext().getLangOptions().ObjCAutoRefCount && + AllocatedType->isObjCLifetimeType()) { + switch (AllocatedType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return true; + } + } + // Otherwise, if the class has a non-trivial destructor, it always // needs a cookie. const CXXRecordDecl *record = - expr->getAllocatedType()->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); + AllocatedType->getBaseElementTypeUnsafe()->getAsCXXRecordDecl(); return (record && !record->hasTrivialDestructor()); } @@ -816,6 +829,22 @@ bool ItaniumCXXABI::NeedsArrayCookie(const CXXDeleteExpr *expr, if (expr->doesUsualArrayDeleteWantSize()) return true; + // Automatic Reference Counting: + // We need an array cookie for pointers with strong or weak lifetime. + if (getContext().getLangOptions().ObjCAutoRefCount && + elementType->isObjCLifetimeType()) { + switch (elementType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + return false; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return true; + } + } + // Otherwise, if the class has a non-trivial destructor, it always // needs a cookie. const CXXRecordDecl *record = @@ -1005,31 +1034,34 @@ void ARMCXXABI::ReadArrayCookie(CodeGenFunction &CGF, /*********************** Static local initialization **************************/ static llvm::Constant *getGuardAcquireFn(CodeGenModule &CGM, - const llvm::PointerType *GuardPtrTy) { + llvm::PointerType *GuardPtrTy) { // int __cxa_guard_acquire(__guard *guard_object); + llvm::Type *ArgTys[] = { GuardPtrTy }; const llvm::FunctionType *FTy = llvm::FunctionType::get(CGM.getTypes().ConvertType(CGM.getContext().IntTy), - GuardPtrTy, /*isVarArg=*/false); + ArgTys, /*isVarArg=*/false); return CGM.CreateRuntimeFunction(FTy, "__cxa_guard_acquire"); } static llvm::Constant *getGuardReleaseFn(CodeGenModule &CGM, - const llvm::PointerType *GuardPtrTy) { + llvm::PointerType *GuardPtrTy) { // void __cxa_guard_release(__guard *guard_object); + llvm::Type *ArgTys[] = { GuardPtrTy }; const llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - GuardPtrTy, /*isVarArg=*/false); + ArgTys, /*isVarArg=*/false); return CGM.CreateRuntimeFunction(FTy, "__cxa_guard_release"); } static llvm::Constant *getGuardAbortFn(CodeGenModule &CGM, - const llvm::PointerType *GuardPtrTy) { + llvm::PointerType *GuardPtrTy) { // void __cxa_guard_abort(__guard *guard_object); + llvm::Type *ArgTys[] = { GuardPtrTy }; const llvm::FunctionType *FTy = llvm::FunctionType::get(llvm::Type::getVoidTy(CGM.getLLVMContext()), - GuardPtrTy, /*isVarArg=*/false); + ArgTys, /*isVarArg=*/false); return CGM.CreateRuntimeFunction(FTy, "__cxa_guard_abort"); } @@ -1039,7 +1071,7 @@ namespace { llvm::GlobalVariable *Guard; CallGuardAbort(llvm::GlobalVariable *Guard) : Guard(Guard) {} - void Emit(CodeGenFunction &CGF, bool IsForEH) { + void Emit(CodeGenFunction &CGF, Flags flags) { CGF.Builder.CreateCall(getGuardAbortFn(CGF.CGM, Guard->getType()), Guard) ->setDoesNotThrow(); } @@ -1055,21 +1087,21 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, // We only need to use thread-safe statics for local variables; // global initialization is always single-threaded. - bool ThreadsafeStatics = (getContext().getLangOptions().ThreadsafeStatics && - D.isLocalVarDecl()); + bool threadsafe = + (getContext().getLangOptions().ThreadsafeStatics && D.isLocalVarDecl()); const llvm::IntegerType *GuardTy; // If we have a global variable with internal linkage and thread-safe statics // are disabled, we can just let the guard variable be of type i8. - bool UseInt8GuardVariable = !ThreadsafeStatics && GV->hasInternalLinkage(); - if (UseInt8GuardVariable) - GuardTy = Builder.getInt8Ty(); - else { + bool useInt8GuardVariable = !threadsafe && GV->hasInternalLinkage(); + if (useInt8GuardVariable) { + GuardTy = CGF.Int8Ty; + } else { // Guard variables are 64 bits in the generic ABI and 32 bits on ARM. - GuardTy = (IsARM ? Builder.getInt32Ty() : Builder.getInt64Ty()); + GuardTy = (IsARM ? CGF.Int32Ty : CGF.Int64Ty); } - const llvm::PointerType *GuardPtrTy = GuardTy->getPointerTo(); + llvm::PointerType *GuardPtrTy = GuardTy->getPointerTo(); // Create the guard variable. llvm::SmallString<256> GuardVName; @@ -1099,7 +1131,7 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, // if (__cxa_guard_acquire(&obj_guard)) // ... // } - if (IsARM && !UseInt8GuardVariable) { + if (IsARM && !useInt8GuardVariable) { llvm::Value *V = Builder.CreateLoad(GuardVariable); V = Builder.CreateAnd(V, Builder.getInt32(1)); IsInitialized = Builder.CreateIsNull(V, "guard.uninitialized"); @@ -1130,13 +1162,16 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, llvm::BasicBlock *InitCheckBlock = CGF.createBasicBlock("init.check"); llvm::BasicBlock *EndBlock = CGF.createBasicBlock("init.end"); + llvm::BasicBlock *NoCheckBlock = EndBlock; + if (threadsafe) NoCheckBlock = CGF.createBasicBlock("init.barrier"); + // Check if the first byte of the guard variable is zero. - Builder.CreateCondBr(IsInitialized, InitCheckBlock, EndBlock); + Builder.CreateCondBr(IsInitialized, InitCheckBlock, NoCheckBlock); CGF.EmitBlock(InitCheckBlock); // Variables used when coping with thread-safe statics and exceptions. - if (ThreadsafeStatics) { + if (threadsafe) { // Call __cxa_guard_acquire. llvm::Value *V = Builder.CreateCall(getGuardAcquireFn(CGM, GuardPtrTy), GuardVariable); @@ -1155,7 +1190,7 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, // Emit the initializer and add a global destructor if appropriate. CGF.EmitCXXGlobalVarDeclInit(D, GV); - if (ThreadsafeStatics) { + if (threadsafe) { // Pop the guard-abort cleanup if we pushed one. CGF.PopCleanupBlock(); @@ -1165,5 +1200,23 @@ void ItaniumCXXABI::EmitGuardedInit(CodeGenFunction &CGF, Builder.CreateStore(llvm::ConstantInt::get(GuardTy, 1), GuardVariable); } + // Emit an acquire memory barrier if using thread-safe statics: + // Itanium ABI: + // An implementation supporting thread-safety on multiprocessor + // systems must also guarantee that references to the initialized + // object do not occur before the load of the initialization flag. + if (threadsafe) { + Builder.CreateBr(EndBlock); + CGF.EmitBlock(NoCheckBlock); + + llvm::Value *_false = Builder.getFalse(); + llvm::Value *_true = Builder.getTrue(); + + Builder.CreateCall5(CGM.getIntrinsic(llvm::Intrinsic::memory_barrier), + /* load-load, load-store */ _true, _true, + /* store-load, store-store */ _false, _false, + /* device or I/O */ _false); + } + CGF.EmitBlock(EndBlock); } diff --git a/lib/CodeGen/TargetInfo.cpp b/lib/CodeGen/TargetInfo.cpp index 043ead7e62d9..df2c1bd98cca 100644 --- a/lib/CodeGen/TargetInfo.cpp +++ b/lib/CodeGen/TargetInfo.cpp @@ -75,7 +75,7 @@ void ABIArgInfo::dump() const { break; case Indirect: OS << "Indirect Align=" << getIndirectAlign() - << " Byal=" << getIndirectByVal() + << " ByVal=" << getIndirectByVal() << " Realign=" << getIndirectRealign(); break; case Expand: @@ -356,9 +356,9 @@ bool UseX86_MMXType(const llvm::Type *IRType) { IRType->getScalarSizeInBits() != 64; } -static const llvm::Type* X86AdjustInlineAsmType(CodeGen::CodeGenFunction &CGF, - llvm::StringRef Constraint, - const llvm::Type* Ty) { +static llvm::Type* X86AdjustInlineAsmType(CodeGen::CodeGenFunction &CGF, + llvm::StringRef Constraint, + llvm::Type* Ty) { if ((Constraint == "y" || Constraint == "&y") && Ty->isVectorTy()) return llvm::Type::getX86_MMXTy(CGF.getLLVMContext()); return Ty; @@ -374,6 +374,7 @@ class X86_32ABIInfo : public ABIInfo { bool IsDarwinVectorABI; bool IsSmallStructInRegABI; + bool IsMMXDisabled; static bool isRegisterSize(unsigned Size) { return (Size == 8 || Size == 16 || Size == 32 || Size == 64); @@ -403,14 +404,15 @@ public: virtual llvm::Value *EmitVAArg(llvm::Value *VAListAddr, QualType Ty, CodeGenFunction &CGF) const; - X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p) - : ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p) {} + X86_32ABIInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m) + : ABIInfo(CGT), IsDarwinVectorABI(d), IsSmallStructInRegABI(p), + IsMMXDisabled(m) {} }; class X86_32TargetCodeGenInfo : public TargetCodeGenInfo { public: - X86_32TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p) - :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p)) {} + X86_32TargetCodeGenInfo(CodeGen::CodeGenTypes &CGT, bool d, bool p, bool m) + :TargetCodeGenInfo(new X86_32ABIInfo(CGT, d, p, m)) {} void SetTargetAttributes(const Decl *D, llvm::GlobalValue *GV, CodeGen::CodeGenModule &CGM) const; @@ -425,9 +427,9 @@ public: bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, llvm::Value *Address) const; - const llvm::Type* adjustInlineAsmType(CodeGen::CodeGenFunction &CGF, - llvm::StringRef Constraint, - const llvm::Type* Ty) const { + llvm::Type* adjustInlineAsmType(CodeGen::CodeGenFunction &CGF, + llvm::StringRef Constraint, + llvm::Type* Ty) const { return X86AdjustInlineAsmType(CGF, Constraint, Ty); } @@ -562,7 +564,7 @@ ABIArgInfo X86_32ABIInfo::classifyReturnType(QualType RetTy) const { } else if (SeltTy->isPointerType()) { // FIXME: It would be really nice if this could come out as the proper // pointer type. - const llvm::Type *PtrTy = llvm::Type::getInt8PtrTy(getVMContext()); + llvm::Type *PtrTy = llvm::Type::getInt8PtrTy(getVMContext()); return ABIArgInfo::getDirect(PtrTy); } else if (SeltTy->isVectorType()) { // 64- and 128-bit vectors are never returned in a @@ -699,8 +701,11 @@ ABIArgInfo X86_32ABIInfo::classifyArgumentType(QualType Ty) const { Size)); } - const llvm::Type *IRType = CGT.ConvertTypeRecursive(Ty); + llvm::Type *IRType = CGT.ConvertType(Ty); if (UseX86_MMXType(IRType)) { + if (IsMMXDisabled) + return ABIArgInfo::getDirect(llvm::IntegerType::get(getVMContext(), + 64)); ABIArgInfo AAI = ABIArgInfo::getDirect(IRType); AAI.setCoerceToType(llvm::Type::getX86_MMXTy(getVMContext())); return AAI; @@ -820,6 +825,22 @@ class X86_64ABIInfo : public ABIInfo { /// should just return Memory for the aggregate). static Class merge(Class Accum, Class Field); + /// postMerge - Implement the X86_64 ABI post merging algorithm. + /// + /// Post merger cleanup, reduces a malformed Hi and Lo pair to + /// final MEMORY or SSE classes when necessary. + /// + /// \param AggregateSize - The size of the current aggregate in + /// the classification process. + /// + /// \param Lo - The classification for the parts of the type + /// residing in the low word of the containing object. + /// + /// \param Hi - The classification for the parts of the type + /// residing in the higher words of the containing object. + /// + void postMerge(unsigned AggregateSize, Class &Lo, Class &Hi) const; + /// classify - Determine the x86_64 register classes in which the /// given type T should be passed. /// @@ -843,13 +864,13 @@ class X86_64ABIInfo : public ABIInfo { /// also be ComplexX87. void classify(QualType T, uint64_t OffsetBase, Class &Lo, Class &Hi) const; - const llvm::Type *Get16ByteVectorType(QualType Ty) const; - const llvm::Type *GetSSETypeAtOffset(const llvm::Type *IRType, - unsigned IROffset, QualType SourceTy, - unsigned SourceOffset) const; - const llvm::Type *GetINTEGERTypeAtOffset(const llvm::Type *IRType, - unsigned IROffset, QualType SourceTy, - unsigned SourceOffset) const; + llvm::Type *GetByteVectorType(QualType Ty) const; + llvm::Type *GetSSETypeAtOffset(llvm::Type *IRType, + unsigned IROffset, QualType SourceTy, + unsigned SourceOffset) const; + llvm::Type *GetINTEGERTypeAtOffset(llvm::Type *IRType, + unsigned IROffset, QualType SourceTy, + unsigned SourceOffset) const; /// getIndirectResult - Give a source type \arg Ty, return a suitable result /// such that the argument will be returned in memory. @@ -921,9 +942,9 @@ public: return false; } - const llvm::Type* adjustInlineAsmType(CodeGen::CodeGenFunction &CGF, - llvm::StringRef Constraint, - const llvm::Type* Ty) const { + llvm::Type* adjustInlineAsmType(CodeGen::CodeGenFunction &CGF, + llvm::StringRef Constraint, + llvm::Type* Ty) const { return X86AdjustInlineAsmType(CGF, Constraint, Ty); } @@ -956,6 +977,39 @@ public: } +void X86_64ABIInfo::postMerge(unsigned AggregateSize, Class &Lo, + Class &Hi) const { + // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done: + // + // (a) If one of the classes is Memory, the whole argument is passed in + // memory. + // + // (b) If X87UP is not preceded by X87, the whole argument is passed in + // memory. + // + // (c) If the size of the aggregate exceeds two eightbytes and the first + // eightbyte isn't SSE or any other eightbyte isn't SSEUP, the whole + // argument is passed in memory. NOTE: This is necessary to keep the + // ABI working for processors that don't support the __m256 type. + // + // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE. + // + // Some of these are enforced by the merging logic. Others can arise + // only with unions; for example: + // union { _Complex double; unsigned; } + // + // Note that clauses (b) and (c) were added in 0.98. + // + if (Hi == Memory) + Lo = Memory; + if (Hi == X87Up && Lo != X87 && honorsRevision0_98()) + Lo = Memory; + if (AggregateSize > 128 && (Lo != SSE || Hi != SSEUp)) + Lo = Memory; + if (Hi == SSEUp && Lo != SSE) + Hi = SSE; +} + X86_64ABIInfo::Class X86_64ABIInfo::merge(Class Accum, Class Field) { // AMD64-ABI 3.2.3p2: Rule 4. Each field of an object is // classified recursively so that always two fields are @@ -1082,7 +1136,14 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, // split. if (OffsetBase && OffsetBase != 64) Hi = Lo; - } else if (Size == 128) { + } else if (Size == 128 || Size == 256) { + // Arguments of 256-bits are split into four eightbyte chunks. The + // least significant one belongs to class SSE and all the others to class + // SSEUP. The original Lo and Hi design considers that types can't be + // greater than 128-bits, so a 64-bit split in Hi and Lo makes sense. + // This design isn't correct for 256-bits, but since there're no cases + // where the upper parts would need to be inspected, avoid adding + // complexity and just consider Hi to match the 64-256 part. Lo = SSE; Hi = SSEUp; } @@ -1121,8 +1182,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, uint64_t Size = getContext().getTypeSize(Ty); // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger - // than two eightbytes, ..., it has class MEMORY. - if (Size > 128) + // than four eightbytes, ..., it has class MEMORY. + if (Size > 256) return; // AMD64-ABI 3.2.3p2: Rule 1. If ..., or it contains unaligned @@ -1137,6 +1198,13 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, Current = NoClass; uint64_t EltSize = getContext().getTypeSize(AT->getElementType()); uint64_t ArraySize = AT->getSize().getZExtValue(); + + // The only case a 256-bit wide vector could be used is when the array + // contains a single 256-bit element. Since Lo and Hi logic isn't extended + // to work for sizes wider than 128, early check and fallback to memory. + if (Size > 128 && EltSize != 256) + return; + for (uint64_t i=0, Offset=OffsetBase; i<ArraySize; ++i, Offset += EltSize) { Class FieldLo, FieldHi; classify(AT->getElementType(), Offset, FieldLo, FieldHi); @@ -1146,9 +1214,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, break; } - // Do post merger cleanup (see below). Only case we worry about is Memory. - if (Hi == Memory) - Lo = Memory; + postMerge(Size, Lo, Hi); assert((Hi != SSEUp || Lo == SSE) && "Invalid SSEUp array classification."); return; } @@ -1157,8 +1223,8 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, uint64_t Size = getContext().getTypeSize(Ty); // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger - // than two eightbytes, ..., it has class MEMORY. - if (Size > 128) + // than four eightbytes, ..., it has class MEMORY. + if (Size > 256) return; // AMD64-ABI 3.2.3p2: Rule 2. If a C++ object has either a non-trivial @@ -1209,9 +1275,17 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, uint64_t Offset = OffsetBase + Layout.getFieldOffset(idx); bool BitField = i->isBitField(); - // AMD64-ABI 3.2.3p2: Rule 1. If ..., or it contains unaligned - // fields, it has class MEMORY. + // AMD64-ABI 3.2.3p2: Rule 1. If the size of an object is larger than + // four eightbytes, or it contains unaligned fields, it has class MEMORY. + // + // The only case a 256-bit wide vector could be used is when the struct + // contains a single 256-bit element. Since Lo and Hi logic isn't extended + // to work for sizes wider than 128, early check and fallback to memory. // + if (Size > 128 && getContext().getTypeSize(i->getType()) != 256) { + Lo = Memory; + return; + } // Note, skip this test for bit-fields, see below. if (!BitField && Offset % getContext().getTypeAlign(i->getType())) { Lo = Memory; @@ -1257,31 +1331,7 @@ void X86_64ABIInfo::classify(QualType Ty, uint64_t OffsetBase, break; } - // AMD64-ABI 3.2.3p2: Rule 5. Then a post merger cleanup is done: - // - // (a) If one of the classes is MEMORY, the whole argument is - // passed in memory. - // - // (b) If X87UP is not preceded by X87, the whole argument is - // passed in memory. - // - // (c) If the size of the aggregate exceeds two eightbytes and the first - // eight-byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole - // argument is passed in memory. - // - // (d) If SSEUP is not preceded by SSE or SSEUP, it is converted to SSE. - // - // Some of these are enforced by the merging logic. Others can arise - // only with unions; for example: - // union { _Complex double; unsigned; } - // - // Note that clauses (b) and (c) were added in 0.98. - if (Hi == Memory) - Lo = Memory; - if (Hi == X87Up && Lo != X87 && honorsRevision0_98()) - Lo = Memory; - if (Hi == SSEUp && Lo != SSE) - Hi = SSE; + postMerge(Size, Lo, Hi); } } @@ -1321,24 +1371,25 @@ ABIArgInfo X86_64ABIInfo::getIndirectResult(QualType Ty) const { return ABIArgInfo::getIndirect(Align); } -/// Get16ByteVectorType - The ABI specifies that a value should be passed in an -/// full vector XMM register. Pick an LLVM IR type that will be passed as a +/// GetByteVectorType - The ABI specifies that a value should be passed in an +/// full vector XMM/YMM register. Pick an LLVM IR type that will be passed as a /// vector register. -const llvm::Type *X86_64ABIInfo::Get16ByteVectorType(QualType Ty) const { - const llvm::Type *IRType = CGT.ConvertTypeRecursive(Ty); +llvm::Type *X86_64ABIInfo::GetByteVectorType(QualType Ty) const { + llvm::Type *IRType = CGT.ConvertType(Ty); // Wrapper structs that just contain vectors are passed just like vectors, // strip them off if present. - const llvm::StructType *STy = dyn_cast<llvm::StructType>(IRType); + llvm::StructType *STy = dyn_cast<llvm::StructType>(IRType); while (STy && STy->getNumElements() == 1) { IRType = STy->getElementType(0); STy = dyn_cast<llvm::StructType>(IRType); } // If the preferred type is a 16-byte vector, prefer to pass it. - if (const llvm::VectorType *VT = dyn_cast<llvm::VectorType>(IRType)){ - const llvm::Type *EltTy = VT->getElementType(); - if (VT->getBitWidth() == 128 && + if (llvm::VectorType *VT = dyn_cast<llvm::VectorType>(IRType)){ + llvm::Type *EltTy = VT->getElementType(); + unsigned BitWidth = VT->getBitWidth(); + if ((BitWidth == 128 || BitWidth == 256) && (EltTy->isFloatTy() || EltTy->isDoubleTy() || EltTy->isIntegerTy(8) || EltTy->isIntegerTy(16) || EltTy->isIntegerTy(32) || EltTy->isIntegerTy(64) || @@ -1466,8 +1517,8 @@ static bool ContainsFloatAtOffset(const llvm::Type *IRType, unsigned IROffset, /// GetSSETypeAtOffset - Return a type that will be passed by the backend in the /// low 8 bytes of an XMM register, corresponding to the SSE class. -const llvm::Type *X86_64ABIInfo:: -GetSSETypeAtOffset(const llvm::Type *IRType, unsigned IROffset, +llvm::Type *X86_64ABIInfo:: +GetSSETypeAtOffset(llvm::Type *IRType, unsigned IROffset, QualType SourceTy, unsigned SourceOffset) const { // The only three choices we have are either double, <2 x float>, or float. We // pass as float if the last 4 bytes is just padding. This happens for @@ -1501,8 +1552,8 @@ GetSSETypeAtOffset(const llvm::Type *IRType, unsigned IROffset, /// SourceTy is the source level type for the entire argument. SourceOffset is /// an offset into this that we're processing (which is always either 0 or 8). /// -const llvm::Type *X86_64ABIInfo:: -GetINTEGERTypeAtOffset(const llvm::Type *IRType, unsigned IROffset, +llvm::Type *X86_64ABIInfo:: +GetINTEGERTypeAtOffset(llvm::Type *IRType, unsigned IROffset, QualType SourceTy, unsigned SourceOffset) const { // If we're dealing with an un-offset LLVM IR type, then it means that we're // returning an 8-byte unit starting with it. See if we can safely use it. @@ -1540,7 +1591,7 @@ GetINTEGERTypeAtOffset(const llvm::Type *IRType, unsigned IROffset, } if (const llvm::ArrayType *ATy = dyn_cast<llvm::ArrayType>(IRType)) { - const llvm::Type *EltTy = ATy->getElementType(); + llvm::Type *EltTy = ATy->getElementType(); unsigned EltSize = getTargetData().getTypeAllocSize(EltTy); unsigned EltOffset = IROffset/EltSize*EltSize; return GetINTEGERTypeAtOffset(EltTy, IROffset-EltOffset, SourceTy, @@ -1566,8 +1617,8 @@ GetINTEGERTypeAtOffset(const llvm::Type *IRType, unsigned IROffset, /// first class aggregate to represent them. For example, if the low part of /// a by-value argument should be passed as i32* and the high part as float, /// return {i32*, float}. -static const llvm::Type * -GetX86_64ByValArgumentPair(const llvm::Type *Lo, const llvm::Type *Hi, +static llvm::Type * +GetX86_64ByValArgumentPair(llvm::Type *Lo, llvm::Type *Hi, const llvm::TargetData &TD) { // In order to correctly satisfy the ABI, we need to the high part to start // at offset 8. If the high and low parts we inferred are both 4-byte types @@ -1594,8 +1645,7 @@ GetX86_64ByValArgumentPair(const llvm::Type *Lo, const llvm::Type *Hi, } } - const llvm::StructType *Result = - llvm::StructType::get(Lo->getContext(), Lo, Hi, NULL); + llvm::StructType *Result = llvm::StructType::get(Lo, Hi, NULL); // Verify that the second element is at an 8-byte offset. @@ -1615,7 +1665,7 @@ classifyReturnType(QualType RetTy) const { assert((Hi != Memory || Lo == Memory) && "Invalid memory classification."); assert((Hi != SSEUp || Lo == SSE) && "Invalid SSEUp classification."); - const llvm::Type *ResType = 0; + llvm::Type *ResType = 0; switch (Lo) { case NoClass: if (Hi == NoClass) @@ -1638,8 +1688,7 @@ classifyReturnType(QualType RetTy) const { // AMD64-ABI 3.2.3p4: Rule 3. If the class is INTEGER, the next // available register of the sequence %rax, %rdx is used. case Integer: - ResType = GetINTEGERTypeAtOffset(CGT.ConvertTypeRecursive(RetTy), 0, - RetTy, 0); + ResType = GetINTEGERTypeAtOffset(CGT.ConvertType(RetTy), 0, RetTy, 0); // If we have a sign or zero extended integer, make sure to return Extend // so that the parameter gets the right LLVM IR attributes. @@ -1657,7 +1706,7 @@ classifyReturnType(QualType RetTy) const { // AMD64-ABI 3.2.3p4: Rule 4. If the class is SSE, the next // available SSE register of the sequence %xmm0, %xmm1 is used. case SSE: - ResType = GetSSETypeAtOffset(CGT.ConvertTypeRecursive(RetTy), 0, RetTy, 0); + ResType = GetSSETypeAtOffset(CGT.ConvertType(RetTy), 0, RetTy, 0); break; // AMD64-ABI 3.2.3p4: Rule 6. If the class is X87, the value is @@ -1671,14 +1720,13 @@ classifyReturnType(QualType RetTy) const { // %st1. case ComplexX87: assert(Hi == ComplexX87 && "Unexpected ComplexX87 classification."); - ResType = llvm::StructType::get(getVMContext(), - llvm::Type::getX86_FP80Ty(getVMContext()), + ResType = llvm::StructType::get(llvm::Type::getX86_FP80Ty(getVMContext()), llvm::Type::getX86_FP80Ty(getVMContext()), NULL); break; } - const llvm::Type *HighPart = 0; + llvm::Type *HighPart = 0; switch (Hi) { // Memory was handled previously and X87 should // never occur as a hi class. @@ -1691,24 +1739,24 @@ classifyReturnType(QualType RetTy) const { break; case Integer: - HighPart = GetINTEGERTypeAtOffset(CGT.ConvertTypeRecursive(RetTy), - 8, RetTy, 8); + HighPart = GetINTEGERTypeAtOffset(CGT.ConvertType(RetTy), 8, RetTy, 8); if (Lo == NoClass) // Return HighPart at offset 8 in memory. return ABIArgInfo::getDirect(HighPart, 8); break; case SSE: - HighPart = GetSSETypeAtOffset(CGT.ConvertTypeRecursive(RetTy), 8, RetTy, 8); + HighPart = GetSSETypeAtOffset(CGT.ConvertType(RetTy), 8, RetTy, 8); if (Lo == NoClass) // Return HighPart at offset 8 in memory. return ABIArgInfo::getDirect(HighPart, 8); break; // AMD64-ABI 3.2.3p4: Rule 5. If the class is SSEUP, the eightbyte - // is passed in the upper half of the last used SSE register. + // is passed in the next available eightbyte chunk if the last used + // vector register. // // SSEUP should always be preceded by SSE, just widen. case SSEUp: assert(Lo == SSE && "Unexpected SSEUp classification."); - ResType = Get16ByteVectorType(RetTy); + ResType = GetByteVectorType(RetTy); break; // AMD64-ABI 3.2.3p4: Rule 7. If the class is X87UP, the value is @@ -1719,8 +1767,7 @@ classifyReturnType(QualType RetTy) const { // preceded by X87. In such situations we follow gcc and pass the // extra bits in an SSE reg. if (Lo != X87) { - HighPart = GetSSETypeAtOffset(CGT.ConvertTypeRecursive(RetTy), - 8, RetTy, 8); + HighPart = GetSSETypeAtOffset(CGT.ConvertType(RetTy), 8, RetTy, 8); if (Lo == NoClass) // Return HighPart at offset 8 in memory. return ABIArgInfo::getDirect(HighPart, 8); } @@ -1748,7 +1795,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, neededInt = 0; neededSSE = 0; - const llvm::Type *ResType = 0; + llvm::Type *ResType = 0; switch (Lo) { case NoClass: if (Hi == NoClass) @@ -1767,6 +1814,8 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, // COMPLEX_X87, it is passed in memory. case X87: case ComplexX87: + if (isRecordWithNonTrivialDestructorOrCopyConstructor(Ty)) + ++neededInt; return getIndirectResult(Ty); case SSEUp: @@ -1780,7 +1829,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, ++neededInt; // Pick an 8-byte type based on the preferred type. - ResType = GetINTEGERTypeAtOffset(CGT.ConvertTypeRecursive(Ty), 0, Ty, 0); + ResType = GetINTEGERTypeAtOffset(CGT.ConvertType(Ty), 0, Ty, 0); // If we have a sign or zero extended integer, make sure to return Extend // so that the parameter gets the right LLVM IR attributes. @@ -1800,19 +1849,14 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, // available SSE register is used, the registers are taken in the // order from %xmm0 to %xmm7. case SSE: { - const llvm::Type *IRType = CGT.ConvertTypeRecursive(Ty); - if (Hi != NoClass || !UseX86_MMXType(IRType)) - ResType = GetSSETypeAtOffset(IRType, 0, Ty, 0); - else - // This is an MMX type. Treat it as such. - ResType = llvm::Type::getX86_MMXTy(getVMContext()); - + llvm::Type *IRType = CGT.ConvertType(Ty); + ResType = GetSSETypeAtOffset(IRType, 0, Ty, 0); ++neededSSE; break; } } - const llvm::Type *HighPart = 0; + llvm::Type *HighPart = 0; switch (Hi) { // Memory was handled previously, ComplexX87 and X87 should // never occur as hi classes, and X87Up must be preceded by X87, @@ -1828,7 +1872,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, case Integer: ++neededInt; // Pick an 8-byte type based on the preferred type. - HighPart = GetINTEGERTypeAtOffset(CGT.ConvertTypeRecursive(Ty), 8, Ty, 8); + HighPart = GetINTEGERTypeAtOffset(CGT.ConvertType(Ty), 8, Ty, 8); if (Lo == NoClass) // Pass HighPart at offset 8 in memory. return ABIArgInfo::getDirect(HighPart, 8); @@ -1838,7 +1882,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, // memory), except in situations involving unions. case X87Up: case SSE: - HighPart = GetSSETypeAtOffset(CGT.ConvertTypeRecursive(Ty), 8, Ty, 8); + HighPart = GetSSETypeAtOffset(CGT.ConvertType(Ty), 8, Ty, 8); if (Lo == NoClass) // Pass HighPart at offset 8 in memory. return ABIArgInfo::getDirect(HighPart, 8); @@ -1851,7 +1895,7 @@ ABIArgInfo X86_64ABIInfo::classifyArgumentType(QualType Ty, unsigned &neededInt, // register. This only happens when 128-bit vectors are passed. case SSEUp: assert(Lo == SSE && "Unexpected SSEUp classification"); - ResType = Get16ByteVectorType(Ty); + ResType = GetByteVectorType(Ty); break; } @@ -2059,10 +2103,10 @@ llvm::Value *X86_64ABIInfo::EmitVAArg(llvm::Value *VAListAddr, QualType Ty, // area, we need to collect the two eightbytes together. llvm::Value *RegAddrLo = CGF.Builder.CreateGEP(RegAddr, fp_offset); llvm::Value *RegAddrHi = CGF.Builder.CreateConstGEP1_32(RegAddrLo, 16); - const llvm::Type *DoubleTy = llvm::Type::getDoubleTy(VMContext); + llvm::Type *DoubleTy = llvm::Type::getDoubleTy(VMContext); const llvm::Type *DblPtrTy = llvm::PointerType::getUnqual(DoubleTy); - const llvm::StructType *ST = llvm::StructType::get(VMContext, DoubleTy, + const llvm::StructType *ST = llvm::StructType::get(DoubleTy, DoubleTy, NULL); llvm::Value *V, *Tmp = CGF.CreateTempAlloca(ST); V = CGF.Builder.CreateLoad(CGF.Builder.CreateBitCast(RegAddrLo, @@ -2277,6 +2321,10 @@ public: return 13; } + llvm::StringRef getARCRetainAutoreleasedReturnValueMarker() const { + return "mov\tr7, r7\t\t@ marker for objc_retainAutoreleaseReturnValue"; + } + bool initDwarfEHRegSizeTable(CodeGen::CodeGenFunction &CGF, llvm::Value *Address) const { CodeGen::CGBuilderTy &Builder = CGF.Builder; @@ -2290,8 +2338,6 @@ public: return false; } - - }; } @@ -2371,9 +2417,8 @@ ABIArgInfo ARMABIInfo::classifyArgumentType(QualType Ty) const { SizeRegs = (getContext().getTypeSize(Ty) + 63) / 64; } - const llvm::Type *STy = - llvm::StructType::get(getVMContext(), - llvm::ArrayType::get(ElemTy, SizeRegs), NULL, NULL); + llvm::Type *STy = + llvm::StructType::get(llvm::ArrayType::get(ElemTy, SizeRegs), NULL); return ABIArgInfo::getDirect(STy); } @@ -3010,10 +3055,12 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::msp430: return *(TheTargetCodeGenInfo = new MSP430TargetCodeGenInfo(Types)); - case llvm::Triple::x86: + case llvm::Triple::x86: { + bool DisableMMX = strcmp(getContext().Target.getABI(), "no-mmx") == 0; + if (Triple.isOSDarwin()) return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo(Types, true, true)); + new X86_32TargetCodeGenInfo(Types, true, true, DisableMMX)); switch (Triple.getOS()) { case llvm::Triple::Cygwin: @@ -3024,12 +3071,13 @@ const TargetCodeGenInfo &CodeGenModule::getTargetCodeGenInfo() { case llvm::Triple::OpenBSD: case llvm::Triple::NetBSD: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo(Types, false, true)); + new X86_32TargetCodeGenInfo(Types, false, true, DisableMMX)); default: return *(TheTargetCodeGenInfo = - new X86_32TargetCodeGenInfo(Types, false, false)); + new X86_32TargetCodeGenInfo(Types, false, false, DisableMMX)); } + } case llvm::Triple::x86_64: switch (Triple.getOS()) { diff --git a/lib/CodeGen/TargetInfo.h b/lib/CodeGen/TargetInfo.h index 4f59eb619e9d..d5e8884cdffe 100644 --- a/lib/CodeGen/TargetInfo.h +++ b/lib/CodeGen/TargetInfo.h @@ -106,11 +106,25 @@ namespace clang { return Address; } - virtual const llvm::Type* adjustInlineAsmType(CodeGen::CodeGenFunction &CGF, - llvm::StringRef Constraint, - const llvm::Type* Ty) const { + virtual llvm::Type* adjustInlineAsmType(CodeGen::CodeGenFunction &CGF, + llvm::StringRef Constraint, + llvm::Type* Ty) const { return Ty; } + + /// Retrieve the address of a function to call immediately before + /// calling objc_retainAutoreleasedReturnValue. The + /// implementation of objc_autoreleaseReturnValue sniffs the + /// instruction stream following its return address to decide + /// whether it's a call to objc_retainAutoreleasedReturnValue. + /// This can be prohibitively expensive, depending on the + /// relocation model, and so on some targets it instead sniffs for + /// a particular instruction sequence. This functions returns + /// that instruction sequence in inline assembly, which will be + /// empty if none is required. + virtual llvm::StringRef getARCRetainAutoreleasedReturnValueMarker() const { + return ""; + } }; } diff --git a/lib/Driver/Arg.cpp b/lib/Driver/Arg.cpp index f1177cf97e28..39b7e5556494 100644 --- a/lib/Driver/Arg.cpp +++ b/lib/Driver/Arg.cpp @@ -113,7 +113,7 @@ void Arg::render(const ArgList &Args, ArgStringList &Output) const { break; case Option::RenderSeparateStyle: - Output.push_back(getOption().getName()); + Output.push_back(getOption().getName().data()); for (unsigned i = 0, e = getNumValues(); i != e; ++i) Output.push_back(getValue(Args, i)); break; diff --git a/lib/Driver/ArgList.cpp b/lib/Driver/ArgList.cpp index 596e2a7529cb..b8af9cc47e09 100644 --- a/lib/Driver/ArgList.cpp +++ b/lib/Driver/ArgList.cpp @@ -287,9 +287,9 @@ Arg *DerivedArgList::MakeSeparateArg(const Arg *BaseArg, const Option *Opt, Arg *DerivedArgList::MakeJoinedArg(const Arg *BaseArg, const Option *Opt, llvm::StringRef Value) const { - unsigned Index = BaseArgs.MakeIndex(Opt->getName() + Value.str()); + unsigned Index = BaseArgs.MakeIndex(Opt->getName().str() + Value.str()); Arg *A = new Arg(Opt, Index, - BaseArgs.getArgString(Index) + strlen(Opt->getName()), + BaseArgs.getArgString(Index) + Opt->getName().size(), BaseArg); SynthesizedArgs.push_back(A); return A; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index eb878a3f1c5e..789f6f7ae9ce 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -458,11 +458,20 @@ bool Driver::HandleImmediateArgs(const Compilation &C) { } llvm::outs() << "\n"; llvm::outs() << "libraries: ="; + + std::string sysroot; + if (Arg *A = C.getArgs().getLastArg(options::OPT__sysroot_EQ)) + sysroot = A->getValue(C.getArgs()); + for (ToolChain::path_list::const_iterator it = TC.getFilePaths().begin(), ie = TC.getFilePaths().end(); it != ie; ++it) { if (it != TC.getFilePaths().begin()) llvm::outs() << ':'; - llvm::outs() << *it; + const char *path = it->c_str(); + if (path[0] == '=') + llvm::outs() << sysroot << path + 1; + else + llvm::outs() << path; } llvm::outs() << "\n"; return false; @@ -942,10 +951,6 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, case phases::Precompile: return new PrecompileJobAction(Input, types::TY_PCH); case phases::Compile: { - bool HasO4 = false; - if (const Arg *A = Args.getLastArg(options::OPT_O_Group)) - HasO4 = A->getOption().matches(options::OPT_O4); - if (Args.hasArg(options::OPT_fsyntax_only)) { return new CompileJobAction(Input, types::TY_Nothing); } else if (Args.hasArg(options::OPT_rewrite_objc)) { @@ -954,9 +959,7 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, return new AnalyzeJobAction(Input, types::TY_Plist); } else if (Args.hasArg(options::OPT_emit_ast)) { return new CompileJobAction(Input, types::TY_AST); - } else if (Args.hasArg(options::OPT_emit_llvm) || - Args.hasFlag(options::OPT_flto, options::OPT_fno_lto, false) || - HasO4) { + } else if (IsUsingLTO(Args)) { types::ID Output = Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC; return new CompileJobAction(Input, Output); @@ -972,6 +975,19 @@ Action *Driver::ConstructPhaseAction(const ArgList &Args, phases::ID Phase, return 0; } +bool Driver::IsUsingLTO(const ArgList &Args) const { + // Check for -emit-llvm or -flto. + if (Args.hasArg(options::OPT_emit_llvm) || + Args.hasFlag(options::OPT_flto, options::OPT_fno_lto, false)) + return true; + + // Check for -O4. + if (const Arg *A = Args.getLastArg(options::OPT_O_Group)) + return A->getOption().matches(options::OPT_O4); + + return false; +} + void Driver::BuildJobs(Compilation &C) const { llvm::PrettyStackTraceString CrashInfo("Building compilation jobs"); @@ -1245,6 +1261,15 @@ const char *Driver::GetNamedOutputPath(Compilation &C, NamedOutput = C.getArgs().MakeArgString(Suffixed.c_str()); } + // If we're saving temps and the temp filename conflicts with the input + // filename, then avoid overwriting input file. + if (!AtTopLevel && C.getArgs().hasArg(options::OPT_save_temps) && + NamedOutput == BaseName) { + std::string TmpName = + GetTemporaryPath(types::getTypeTempSuffix(JA.getType())); + return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str())); + } + // As an annoying special case, PCH generation doesn't strip the pathname. if (JA.getType() == types::TY_PCH) { llvm::sys::path::remove_filename(BasePath); diff --git a/lib/Driver/Option.cpp b/lib/Driver/Option.cpp index a992cef3d239..90d21a3d0b17 100644 --- a/lib/Driver/Option.cpp +++ b/lib/Driver/Option.cpp @@ -144,7 +144,7 @@ FlagOption::FlagOption(OptSpecifier ID, const char *Name, Arg *FlagOption::accept(const ArgList &Args, unsigned &Index) const { // Matches iff this is an exact match. // FIXME: Avoid strlen. - if (strlen(getName()) != strlen(Args.getArgString(Index))) + if (getName().size() != strlen(Args.getArgString(Index))) return 0; return new Arg(getUnaliasedOption(), Index++); @@ -157,7 +157,7 @@ JoinedOption::JoinedOption(OptSpecifier ID, const char *Name, Arg *JoinedOption::accept(const ArgList &Args, unsigned &Index) const { // Always matches. - const char *Value = Args.getArgString(Index) + strlen(getName()); + const char *Value = Args.getArgString(Index) + getName().size(); return new Arg(getUnaliasedOption(), Index++, Value); } @@ -170,7 +170,7 @@ CommaJoinedOption::CommaJoinedOption(OptSpecifier ID, const char *Name, Arg *CommaJoinedOption::accept(const ArgList &Args, unsigned &Index) const { // Always matches. - const char *Str = Args.getArgString(Index) + strlen(getName()); + const char *Str = Args.getArgString(Index) + getName().size(); Arg *A = new Arg(getUnaliasedOption(), Index++); // Parse out the comma separated values. @@ -205,7 +205,7 @@ SeparateOption::SeparateOption(OptSpecifier ID, const char *Name, Arg *SeparateOption::accept(const ArgList &Args, unsigned &Index) const { // Matches iff this is an exact match. // FIXME: Avoid strlen. - if (strlen(getName()) != strlen(Args.getArgString(Index))) + if (getName().size() != strlen(Args.getArgString(Index))) return 0; Index += 2; @@ -225,7 +225,7 @@ MultiArgOption::MultiArgOption(OptSpecifier ID, const char *Name, Arg *MultiArgOption::accept(const ArgList &Args, unsigned &Index) const { // Matches iff this is an exact match. // FIXME: Avoid strlen. - if (strlen(getName()) != strlen(Args.getArgString(Index))) + if (getName().size() != strlen(Args.getArgString(Index))) return 0; Index += 1 + NumArgs; @@ -250,8 +250,8 @@ Arg *JoinedOrSeparateOption::accept(const ArgList &Args, unsigned &Index) const { // If this is not an exact match, it is a joined arg. // FIXME: Avoid strlen. - if (strlen(getName()) != strlen(Args.getArgString(Index))) { - const char *Value = Args.getArgString(Index) + strlen(getName()); + if (getName().size() != strlen(Args.getArgString(Index))) { + const char *Value = Args.getArgString(Index) + getName().size(); return new Arg(this, Index++, Value); } @@ -279,6 +279,6 @@ Arg *JoinedAndSeparateOption::accept(const ArgList &Args, return 0; return new Arg(getUnaliasedOption(), Index - 2, - Args.getArgString(Index-2)+strlen(getName()), + Args.getArgString(Index-2)+getName().size(), Args.getArgString(Index-1)); } diff --git a/lib/Driver/ToolChain.cpp b/lib/Driver/ToolChain.cpp index d9199154104a..74b65918f34f 100644 --- a/lib/Driver/ToolChain.cpp +++ b/lib/Driver/ToolChain.cpp @@ -15,7 +15,9 @@ #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/HostInfo.h" +#include "clang/Driver/ObjCRuntime.h" #include "clang/Driver/Options.h" +#include "llvm/Support/ErrorHandling.h" using namespace clang::driver; @@ -47,6 +49,25 @@ bool ToolChain::HasNativeLLVMSupport() const { return false; } +void ToolChain::configureObjCRuntime(ObjCRuntime &runtime) const { + switch (runtime.getKind()) { + case ObjCRuntime::NeXT: + // Assume a minimal NeXT runtime. + runtime.HasARC = false; + runtime.HasWeak = false; + runtime.HasTerminate = false; + return; + + case ObjCRuntime::GNU: + // Assume a maximal GNU runtime. + runtime.HasARC = true; + runtime.HasWeak = true; + runtime.HasTerminate = false; // to be added + return; + } + llvm_unreachable("invalid runtime kind!"); +} + /// getARMTargetCPU - Get the (LLVM) name of the ARM cpu we are targeting. // // FIXME: tblgen this. @@ -201,18 +222,21 @@ ToolChain::CXXStdlibType ToolChain::GetCXXStdlibType(const ArgList &Args) const{ } void ToolChain::AddClangCXXStdlibIncludeArgs(const ArgList &Args, - ArgStringList &CmdArgs) const { + ArgStringList &CmdArgs, + bool ObjCXXAutoRefCount) const { CXXStdlibType Type = GetCXXStdlibType(Args); + // Header search paths are handled by the mass of goop in InitHeaderSearch. + switch (Type) { case ToolChain::CST_Libcxx: - CmdArgs.push_back("-nostdinc++"); - CmdArgs.push_back("-cxx-isystem"); - CmdArgs.push_back("/usr/include/c++/v1"); + if (ObjCXXAutoRefCount) + CmdArgs.push_back("-fobjc-arc-cxxlib=libc++"); break; case ToolChain::CST_Libstdcxx: - // Currently handled by the mass of goop in InitHeaderSearch. + if (ObjCXXAutoRefCount) + CmdArgs.push_back("-fobjc-arc-cxxlib=libstdc++"); break; } } diff --git a/lib/Driver/ToolChains.cpp b/lib/Driver/ToolChains.cpp index ca613e3d6c84..1619ef8f84a9 100644 --- a/lib/Driver/ToolChains.cpp +++ b/lib/Driver/ToolChains.cpp @@ -19,6 +19,7 @@ #include "clang/Driver/Driver.h" #include "clang/Driver/DriverDiagnostic.h" #include "clang/Driver/HostInfo.h" +#include "clang/Driver/ObjCRuntime.h" #include "clang/Driver/OptTable.h" #include "clang/Driver/Option.h" #include "clang/Driver/Options.h" @@ -26,6 +27,7 @@ #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" @@ -35,13 +37,16 @@ #include <cstdlib> // ::getenv +#include "llvm/Config/config.h" // for CXX_INCLUDE_ROOT + using namespace clang::driver; using namespace clang::driver::toolchains; /// Darwin - Darwin tool chain for i386 and x86_64. Darwin::Darwin(const HostInfo &Host, const llvm::Triple& Triple) - : ToolChain(Host, Triple), TargetInitialized(false) + : ToolChain(Host, Triple), TargetInitialized(false), + ARCRuntimeForSimulator(ARCSimulator_None) { // Compute the initial Darwin version based on the host. bool HadExtra; @@ -70,6 +75,39 @@ bool Darwin::HasNativeLLVMSupport() const { return true; } +bool Darwin::hasARCRuntime() const { + // FIXME: Remove this once there is a proper way to detect an ARC runtime + // for the simulator. + switch (ARCRuntimeForSimulator) { + case ARCSimulator_None: + break; + case ARCSimulator_HasARCRuntime: + return true; + case ARCSimulator_NoARCRuntime: + return false; + } + + if (isTargetIPhoneOS()) + return !isIPhoneOSVersionLT(5); + else + return !isMacosxVersionLT(10, 7); +} + +/// Darwin provides an ARC runtime starting in MacOS X 10.7 and iOS 5.0. +void Darwin::configureObjCRuntime(ObjCRuntime &runtime) const { + if (runtime.getKind() != ObjCRuntime::NeXT) + return ToolChain::configureObjCRuntime(runtime); + + runtime.HasARC = runtime.HasWeak = hasARCRuntime(); + + // So far, objc_terminate is only available in iOS 5. + // FIXME: do the simulator logic properly. + if (!ARCRuntimeForSimulator && isTargetIPhoneOS()) + runtime.HasTerminate = !isIPhoneOSVersionLT(5); + else + runtime.HasTerminate = false; +} + // FIXME: Can we tablegen this? static const char *GetArmArchForMArch(llvm::StringRef Value) { if (Value == "armv6k") @@ -320,6 +358,45 @@ void DarwinClang::AddLinkSearchPathArgs(const ArgList &Args, CmdArgs.push_back(Args.MakeArgString("-L" + P.str())); } +void DarwinClang::AddLinkARCArgs(const ArgList &Args, + ArgStringList &CmdArgs) const { + + CmdArgs.push_back("-force_load"); + llvm::sys::Path P(getDriver().ClangExecutable); + P.eraseComponent(); // 'clang' + P.eraseComponent(); // 'bin' + P.appendComponent("lib"); + P.appendComponent("arc"); + P.appendComponent("libarclite_"); + std::string s = P.str(); + // Mash in the platform. + if (isTargetIPhoneOS()) + s += "iphoneos"; + // FIXME: isTargetIphoneOSSimulator() is not returning true. + else if (ARCRuntimeForSimulator != ARCSimulator_None) + s += "iphonesimulator"; + else + s += "macosx"; + s += ".a"; + + CmdArgs.push_back(Args.MakeArgString(s)); +} + +void DarwinClang::AddLinkRuntimeLib(const ArgList &Args, + ArgStringList &CmdArgs, + const char *DarwinStaticLib) const { + llvm::sys::Path P(getDriver().ResourceDir); + P.appendComponent("lib"); + P.appendComponent("darwin"); + P.appendComponent(DarwinStaticLib); + + // For now, allow missing resource libraries to support developers who may + // not have compiler-rt checked out or integrated into their build. + bool Exists; + if (!llvm::sys::fs::exists(P.str(), Exists) && Exists) + CmdArgs.push_back(Args.MakeArgString(P.str())); +} + void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const { // Darwin doesn't support real static executables, don't link any runtime @@ -341,7 +418,6 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, CmdArgs.push_back("-lSystem"); // Select the dynamic runtime library and the target specific static library. - const char *DarwinStaticLib = 0; if (isTargetIPhoneOS()) { // If we are compiling as iOS / simulator, don't attempt to link libgcc_s.1, // it never went into the SDK. @@ -349,7 +425,7 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, CmdArgs.push_back("-lgcc_s.1"); // We currently always need a static runtime library for iOS. - DarwinStaticLib = "libclang_rt.ios.a"; + AddLinkRuntimeLib(Args, CmdArgs, "libclang_rt.ios.a"); } else { // The dynamic runtime library was merged with libSystem for 10.6 and // beyond; only 10.4 and 10.5 need an additional runtime library. @@ -367,26 +443,42 @@ void DarwinClang::AddLinkRuntimeLibArgs(const ArgList &Args, // libSystem. Therefore, we still must provide a runtime library just for // the tiny tiny handful of projects that *might* use that symbol. if (isMacosxVersionLT(10, 5)) { - DarwinStaticLib = "libclang_rt.10.4.a"; + AddLinkRuntimeLib(Args, CmdArgs, "libclang_rt.10.4.a"); } else { if (getTriple().getArch() == llvm::Triple::x86) - DarwinStaticLib = "libclang_rt.eprintf.a"; + AddLinkRuntimeLib(Args, CmdArgs, "libclang_rt.eprintf.a"); + AddLinkRuntimeLib(Args, CmdArgs, "libclang_rt.osx.a"); } } +} - /// Add the target specific static library, if needed. - if (DarwinStaticLib) { - llvm::sys::Path P(getDriver().ResourceDir); - P.appendComponent("lib"); - P.appendComponent("darwin"); - P.appendComponent(DarwinStaticLib); - - // For now, allow missing resource libraries to support developers who may - // not have compiler-rt checked out or integrated into their build. - bool Exists; - if (!llvm::sys::fs::exists(P.str(), Exists) && Exists) - CmdArgs.push_back(Args.MakeArgString(P.str())); - } +static inline llvm::StringRef SimulatorVersionDefineName() { + return "__IPHONE_OS_VERSION_MIN_REQUIRED"; +} + +/// \brief Parse the simulator version define: +/// __IPHONE_OS_VERSION_MIN_REQUIRED=([0-9])([0-9][0-9])([0-9][0-9]) +// and return the grouped values as integers, e.g: +// __IPHONE_OS_VERSION_MIN_REQUIRED=40201 +// will return Major=4, Minor=2, Micro=1. +static bool GetVersionFromSimulatorDefine(llvm::StringRef define, + unsigned &Major, unsigned &Minor, + unsigned &Micro) { + assert(define.startswith(SimulatorVersionDefineName())); + llvm::StringRef name, version; + llvm::tie(name, version) = define.split('='); + if (version.empty()) + return false; + std::string verstr = version.str(); + char *end; + unsigned num = (unsigned) strtol(verstr.c_str(), &end, 10); + if (*end != '\0') + return false; + Major = num / 10000; + num = num % 10000; + Minor = num / 100; + Micro = num % 100; + return true; } void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { @@ -396,6 +488,27 @@ void Darwin::AddDeploymentTarget(DerivedArgList &Args) const { Arg *iOSVersion = Args.getLastArg(options::OPT_miphoneos_version_min_EQ); Arg *iOSSimVersion = Args.getLastArg( options::OPT_mios_simulator_version_min_EQ); + + // FIXME: HACK! When compiling for the simulator we don't get a + // '-miphoneos-version-min' to help us know whether there is an ARC runtime + // or not; try to parse a __IPHONE_OS_VERSION_MIN_REQUIRED + // define passed in command-line. + if (!iOSVersion) { + for (arg_iterator it = Args.filtered_begin(options::OPT_D), + ie = Args.filtered_end(); it != ie; ++it) { + llvm::StringRef define = (*it)->getValue(Args); + if (define.startswith(SimulatorVersionDefineName())) { + unsigned Major, Minor, Micro; + if (GetVersionFromSimulatorDefine(define, Major, Minor, Micro) && + Major < 10 && Minor < 100 && Micro < 100) { + ARCRuntimeForSimulator = Major < 5 ? ARCSimulator_NoARCRuntime + : ARCSimulator_HasARCRuntime; + } + break; + } + } + } + if (OSXVersion && (iOSVersion || iOSSimVersion)) { getDriver().Diag(clang::diag::err_drv_argument_not_allowed_with) << OSXVersion->getAsString(Args) @@ -587,8 +700,13 @@ DerivedArgList *Darwin::TranslateArgs(const DerivedArgList &Args, Arg *A = *it; if (A->getOption().matches(options::OPT_Xarch__)) { + // Skip this argument unless the architecture matches either the toolchain + // triple arch, or the arch being bound. + // // FIXME: Canonicalize name. - if (getArchName() != A->getValue(Args, 0)) + llvm::StringRef XarchArch = A->getValue(Args, 0); + if (!(XarchArch == getArchName() || + (BoundArch && XarchArch == BoundArch))) continue; Arg *OriginalArg = A; @@ -1359,7 +1477,7 @@ static std::string findGCCBaseLibDir(const std::string &GccTriple) { ret.append(Version); return ret; } - static const char* GccVersions[] = {"4.6.0", "4.6", + static const char* GccVersions[] = {"4.6.1", "4.6.0", "4.6", "4.5.2", "4.5.1", "4.5", "4.4.5", "4.4.4", "4.4.3", "4.4", "4.3.4", "4.3.3", "4.3.2", "4.3", @@ -1421,6 +1539,9 @@ Linux::Linux(const HostInfo &Host, const llvm::Triple &Triple) else if (!llvm::sys::fs::exists("/usr/lib/gcc/x86_64-pc-linux-gnu", Exists) && Exists) GccTriple = "x86_64-pc-linux-gnu"; + else if (!llvm::sys::fs::exists("/usr/lib/gcc/x86_64-redhat-linux6E", + Exists) && Exists) + GccTriple = "x86_64-redhat-linux6E"; else if (!llvm::sys::fs::exists("/usr/lib/gcc/x86_64-redhat-linux", Exists) && Exists) GccTriple = "x86_64-redhat-linux"; diff --git a/lib/Driver/ToolChains.h b/lib/Driver/ToolChains.h index ace9b847919f..d68016b3a240 100644 --- a/lib/Driver/ToolChains.h +++ b/lib/Driver/ToolChains.h @@ -57,6 +57,16 @@ private: // the argument translation business. mutable bool TargetInitialized; + // FIXME: Remove this once there is a proper way to detect an ARC runtime + // for the simulator. + public: + mutable enum { + ARCSimulator_None, + ARCSimulator_HasARCRuntime, + ARCSimulator_NoARCRuntime + } ARCRuntimeForSimulator; + +private: /// Whether we are targeting iPhoneOS target. mutable bool TargetIsIPhoneOS; @@ -70,6 +80,8 @@ private: /// initialized. std::string MacosxVersionMin; + bool hasARCRuntime() const; + private: void AddDeploymentTarget(DerivedArgList &Args) const; @@ -157,11 +169,15 @@ public: virtual void AddLinkSearchPathArgs(const ArgList &Args, ArgStringList &CmdArgs) const = 0; + /// AddLinkARCArgs - Add the linker arguments to link the ARC runtime library. + virtual void AddLinkARCArgs(const ArgList &Args, + ArgStringList &CmdArgs) const = 0; + /// AddLinkRuntimeLibArgs - Add the linker arguments to link the compiler /// runtime library. virtual void AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const = 0; - + /// } /// @name ToolChain Implementation /// { @@ -170,6 +186,8 @@ public: virtual bool HasNativeLLVMSupport() const; + virtual void configureObjCRuntime(ObjCRuntime &runtime) const; + virtual DerivedArgList *TranslateArgs(const DerivedArgList &Args, const char *BoundArch) const; @@ -250,13 +268,17 @@ public: virtual void AddLinkRuntimeLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const; - + void AddLinkRuntimeLib(const ArgList &Args, ArgStringList &CmdArgs, + const char *DarwinStaticLib) const; + virtual void AddCXXStdlibLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const; virtual void AddCCKextLibArgs(const ArgList &Args, ArgStringList &CmdArgs) const; + virtual void AddLinkARCArgs(const ArgList &Args, + ArgStringList &CmdArgs) const; /// } }; diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index 8cd7adc42e84..eb17609d6c34 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -17,6 +17,7 @@ #include "clang/Driver/Compilation.h" #include "clang/Driver/Job.h" #include "clang/Driver/HostInfo.h" +#include "clang/Driver/ObjCRuntime.h" #include "clang/Driver/Option.h" #include "clang/Driver/Options.h" #include "clang/Driver/ToolChain.h" @@ -30,6 +31,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Host.h" #include "llvm/Support/Process.h" +#include "llvm/Support/ErrorHandling.h" #include "InputInfo.h" #include "ToolChains.h" @@ -147,20 +149,37 @@ static void AddLinkerInputs(const ToolChain &TC, } } +/// \brief Determine whether Objective-C automated reference counting is +/// enabled. +static bool isObjCAutoRefCount(const ArgList &Args) { + return Args.hasFlag(options::OPT_fobjc_arc, options::OPT_fno_objc_arc, false); +} + static void addProfileRT(const ToolChain &TC, const ArgList &Args, - ArgStringList &CmdArgs) { - if (Args.hasArg(options::OPT_fprofile_arcs) || - Args.hasArg(options::OPT_fprofile_generate) || - Args.hasArg(options::OPT_fcreate_profile) || - Args.hasArg(options::OPT_coverage)) { - // GCC links libgcov.a by adding -L<inst>/gcc/lib/gcc/<triple>/<ver> -lgcov - // to the link line. We cannot do the same thing because unlike gcov - // there is a libprofile_rt.so. We used to use the -l:libprofile_rt.a - // syntax, but that is not supported by old linkers. - const char *lib = Args.MakeArgString(TC.getDriver().Dir + "/../lib/" + - "libprofile_rt.a"); - CmdArgs.push_back(lib); + ArgStringList &CmdArgs, + llvm::Triple Triple) { + if (!(Args.hasArg(options::OPT_fprofile_arcs) || + Args.hasArg(options::OPT_fprofile_generate) || + Args.hasArg(options::OPT_fcreate_profile) || + Args.hasArg(options::OPT_coverage))) + return; + + // GCC links libgcov.a by adding -L<inst>/gcc/lib/gcc/<triple>/<ver> -lgcov to + // the link line. We cannot do the same thing because unlike gcov there is a + // libprofile_rt.so. We used to use the -l:libprofile_rt.a syntax, but that is + // not supported by old linkers. + llvm::Twine ProfileRT = + llvm::Twine(TC.getDriver().Dir) + "/../lib/" + "libprofile_rt.a"; + + if (Triple.getOS() == llvm::Triple::Darwin) { + // On Darwin, if the static library doesn't exist try the dylib. + bool Exists; + if (llvm::sys::fs::exists(ProfileRT.str(), Exists) || !Exists) + ProfileRT = + llvm::Twine(TC.getDriver().Dir) + "/../lib/" + "libprofile_rt.dylib"; } + + CmdArgs.push_back(Args.MakeArgString(ProfileRT)); } void Clang::AddPreprocessingOptions(const Driver &D, @@ -223,6 +242,13 @@ void Clang::AddPreprocessingOptions(const Driver &D, CmdArgs.push_back("-sys-header-deps"); } + if (Args.hasArg(options::OPT_MG)) { + if (!A || A->getOption().matches(options::OPT_MD) || + A->getOption().matches(options::OPT_MMD)) + D.Diag(clang::diag::err_drv_mg_requires_m_or_mm); + CmdArgs.push_back("-MG"); + } + Args.AddLastArg(CmdArgs, options::OPT_MP); // Convert all -MQ <target> args to -MT <quoted target> @@ -318,8 +344,13 @@ void Clang::AddPreprocessingOptions(const Driver &D, // Add C++ include arguments, if needed. types::ID InputType = Inputs[0].getType(); - if (types::isCXX(InputType)) - getToolChain().AddClangCXXStdlibIncludeArgs(Args, CmdArgs); + if (types::isCXX(InputType)) { + bool ObjCXXAutoRefCount + = types::isObjC(InputType) && isObjCAutoRefCount(Args); + getToolChain().AddClangCXXStdlibIncludeArgs(Args, CmdArgs, + ObjCXXAutoRefCount); + Args.AddAllArgs(CmdArgs, options::OPT_stdlib_EQ); + } // Add -Wp, and -Xassembler if using the preprocessor. @@ -737,7 +768,6 @@ void Clang::AddSparcTargetArgs(const ArgList &Args, // // FIXME: This changes CPP defines, we need -target-soft-float. CmdArgs.push_back("-msoft-float"); - CmdArgs.push_back("soft"); CmdArgs.push_back("-target-feature"); CmdArgs.push_back("+soft-float"); } else { @@ -816,6 +846,14 @@ void Clang::AddX86TargetArgs(const ArgList &Args, CmdArgs.push_back(CPUName); } + // The required algorithm here is slightly strange: the options are applied + // in order (so -mno-sse -msse2 disables SSE3), but any option that gets + // directly overridden later is ignored (so "-mno-sse -msse2 -mno-sse2 -msse" + // is equivalent to "-mno-sse2 -msse"). The -cc1 handling deals with the + // former correctly, but not the latter; handle directly-overridden + // attributes here. + llvm::StringMap<unsigned> PrevFeature; + std::vector<const char*> Features; for (arg_iterator it = Args.filtered_begin(options::OPT_m_x86_Features_Group), ie = Args.filtered_end(); it != ie; ++it) { llvm::StringRef Name = (*it)->getOption().getName(); @@ -829,25 +867,34 @@ void Clang::AddX86TargetArgs(const ArgList &Args, if (IsNegative) Name = Name.substr(3); - CmdArgs.push_back("-target-feature"); - CmdArgs.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); + unsigned& Prev = PrevFeature[Name]; + if (Prev) + Features[Prev - 1] = 0; + Prev = Features.size() + 1; + Features.push_back(Args.MakeArgString((IsNegative ? "-" : "+") + Name)); + } + for (unsigned i = 0; i < Features.size(); i++) { + if (Features[i]) { + CmdArgs.push_back("-target-feature"); + CmdArgs.push_back(Features[i]); + } } } static bool -shouldUseExceptionTablesForObjCExceptions(const ArgList &Args, +shouldUseExceptionTablesForObjCExceptions(unsigned objcABIVersion, const llvm::Triple &Triple) { // We use the zero-cost exception tables for Objective-C if the non-fragile // ABI is enabled or when compiling for x86_64 and ARM on Snow Leopard and // later. - if (Args.hasArg(options::OPT_fobjc_nonfragile_abi)) + if (objcABIVersion >= 2) return true; if (Triple.getOS() != llvm::Triple::Darwin) return false; - return (Triple.getDarwinMajorNumber() >= 9 && + return (!Triple.isMacOSXVersionLT(10,5) && (Triple.getArch() == llvm::Triple::x86_64 || Triple.getArch() == llvm::Triple::arm)); } @@ -860,6 +907,7 @@ shouldUseExceptionTablesForObjCExceptions(const ArgList &Args, static void addExceptionArgs(const ArgList &Args, types::ID InputType, const llvm::Triple &Triple, bool KernelOrKext, bool IsRewriter, + unsigned objcABIVersion, ArgStringList &CmdArgs) { if (KernelOrKext) return; @@ -896,7 +944,7 @@ static void addExceptionArgs(const ArgList &Args, types::ID InputType, CmdArgs.push_back("-fobjc-exceptions"); ShouldUseExceptionTables |= - shouldUseExceptionTablesForObjCExceptions(Args, Triple); + shouldUseExceptionTablesForObjCExceptions(objcABIVersion, Triple); } if (types::isCXX(InputType)) { @@ -1040,6 +1088,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, } else if (Value == "--fatal-warnings") { CmdArgs.push_back("-mllvm"); CmdArgs.push_back("-fatal-assembler-warnings"); + } else if (Value == "--noexecstack") { + CmdArgs.push_back("-mnoexecstack"); } else { D.Diag(clang::diag::err_drv_unsupported_option_argument) << A->getOption().getName() << Value; @@ -1311,8 +1361,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, types::ID InputType = Inputs[0].getType(); if (!Args.hasArg(options::OPT_fallow_unsupported)) { Arg *Unsupported; - if ((Unsupported = Args.getLastArg(options::OPT_MG)) || - (Unsupported = Args.getLastArg(options::OPT_iframework))) + if ((Unsupported = Args.getLastArg(options::OPT_iframework))) D.Diag(clang::diag::err_drv_clang_unsupported) << Unsupported->getOption().getName(); @@ -1378,6 +1427,28 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, Args.AddLastArg(CmdArgs, options::OPT_working_directory); + if (!Args.hasArg(options::OPT_fno_objc_arc)) { + if (const Arg *A = Args.getLastArg(options::OPT_ccc_arcmt_check, + options::OPT_ccc_arcmt_modify, + options::OPT_ccc_arcmt_migrate)) { + switch (A->getOption().getID()) { + default: + llvm_unreachable("missed a case"); + case options::OPT_ccc_arcmt_check: + CmdArgs.push_back("-arcmt-check"); + break; + case options::OPT_ccc_arcmt_modify: + CmdArgs.push_back("-arcmt-modify"); + break; + case options::OPT_ccc_arcmt_migrate: + CmdArgs.push_back("-arcmt-migrate"); + CmdArgs.push_back("-arcmt-migrate-directory"); + CmdArgs.push_back(A->getValue(Args)); + break; + } + } + } + // Add preprocessing options like -I, -D, etc. if we are using the // preprocessor. // @@ -1542,27 +1613,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, options::OPT_fno_lax_vector_conversions)) CmdArgs.push_back("-fno-lax-vector-conversions"); - // -fobjc-infer-related-result-type is the default. - if (Args.hasFlag(options::OPT_fobjc_infer_related_result_type, - options::OPT_fno_objc_infer_related_result_type, - /*Default=*/true)) - CmdArgs.push_back("-fobjc-infer-related-result-type"); - - // Handle -fobjc-gc and -fobjc-gc-only. They are exclusive, and -fobjc-gc-only - // takes precedence. - const Arg *GCArg = Args.getLastArg(options::OPT_fobjc_gc_only); - if (!GCArg) - GCArg = Args.getLastArg(options::OPT_fobjc_gc); - if (GCArg) { - if (getToolChain().SupportsObjCGC()) { - GCArg->render(Args, CmdArgs); - } else { - // FIXME: We should move this to a hard error. - D.Diag(clang::diag::warn_drv_objc_gc_unsupported) - << GCArg->getAsString(Args); - } - } - if (Args.getLastArg(options::OPT_fapple_kext)) CmdArgs.push_back("-fapple-kext"); @@ -1660,13 +1710,6 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, false)) CmdArgs.push_back("-fno-elide-constructors"); - // Add exception args. - addExceptionArgs(Args, InputType, getToolChain().getTriple(), - KernelOrKext, IsRewriter, CmdArgs); - - if (getToolChain().UseSjLjExceptions()) - CmdArgs.push_back("-fsjlj-exceptions"); - // -frtti is default. if (KernelOrKext || !Args.hasFlag(options::OPT_frtti, options::OPT_fno_rtti)) @@ -1734,30 +1777,41 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, false)) CmdArgs.push_back("-fgnu89-inline"); - // -fnext-runtime defaults to on Darwin and when rewriting Objective-C, and is - // -the -cc1 default. - bool NeXTRuntimeIsDefault = - IsRewriter || getToolChain().getTriple().getOS() == llvm::Triple::Darwin; - if (!Args.hasFlag(options::OPT_fnext_runtime, options::OPT_fgnu_runtime, - NeXTRuntimeIsDefault)) - CmdArgs.push_back("-fgnu-runtime"); - // -fobjc-nonfragile-abi=0 is default. + ObjCRuntime objCRuntime; + unsigned objcABIVersion = 0; if (types::isObjC(InputType)) { + bool NeXTRuntimeIsDefault + = (IsRewriter || getToolChain().getTriple().isOSDarwin()); + if (Args.hasFlag(options::OPT_fnext_runtime, options::OPT_fgnu_runtime, + NeXTRuntimeIsDefault)) { + objCRuntime.setKind(ObjCRuntime::NeXT); + } else { + CmdArgs.push_back("-fgnu-runtime"); + objCRuntime.setKind(ObjCRuntime::GNU); + } + getToolChain().configureObjCRuntime(objCRuntime); + if (objCRuntime.HasARC) + CmdArgs.push_back("-fobjc-runtime-has-arc"); + if (objCRuntime.HasWeak) + CmdArgs.push_back("-fobjc-runtime-has-weak"); + if (objCRuntime.HasTerminate) + CmdArgs.push_back("-fobjc-runtime-has-terminate"); + // Compute the Objective-C ABI "version" to use. Version numbers are // slightly confusing for historical reasons: // 1 - Traditional "fragile" ABI // 2 - Non-fragile ABI, version 1 // 3 - Non-fragile ABI, version 2 - unsigned Version = 1; + objcABIVersion = 1; // If -fobjc-abi-version= is present, use that to set the version. if (Arg *A = Args.getLastArg(options::OPT_fobjc_abi_version_EQ)) { if (llvm::StringRef(A->getValue(Args)) == "1") - Version = 1; + objcABIVersion = 1; else if (llvm::StringRef(A->getValue(Args)) == "2") - Version = 2; + objcABIVersion = 2; else if (llvm::StringRef(A->getValue(Args)) == "3") - Version = 3; + objcABIVersion = 3; else D.Diag(clang::diag::err_drv_clang_unsupported) << A->getAsString(Args); } else { @@ -1783,13 +1837,13 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, << A->getAsString(Args); } - Version = 1 + NonFragileABIVersion; + objcABIVersion = 1 + NonFragileABIVersion; } else { - Version = 1; + objcABIVersion = 1; } } - if (Version == 2 || Version == 3) { + if (objcABIVersion == 2 || objcABIVersion == 3) { CmdArgs.push_back("-fobjc-nonfragile-abi"); // -fobjc-dispatch-method is only relevant with the nonfragile-abi, and @@ -1818,6 +1872,51 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, #endif } + // Allow -fno-objc-arr to trump -fobjc-arr/-fobjc-arc. + // NOTE: This logic is duplicated in ToolChains.cpp. + bool ARC = isObjCAutoRefCount(Args); + if (ARC) { + CmdArgs.push_back("-fobjc-arc"); + + // Allow the user to enable full exceptions code emission. + // We define off for Objective-CC, on for Objective-C++. + if (Args.hasFlag(options::OPT_fobjc_arc_exceptions, + options::OPT_fno_objc_arc_exceptions, + /*default*/ types::isCXX(InputType))) + CmdArgs.push_back("-fobjc-arc-exceptions"); + } + + // -fobjc-infer-related-result-type is the default, except in the Objective-C + // rewriter. + if (IsRewriter) + CmdArgs.push_back("-fno-objc-infer-related-result-type"); + + // Handle -fobjc-gc and -fobjc-gc-only. They are exclusive, and -fobjc-gc-only + // takes precedence. + const Arg *GCArg = Args.getLastArg(options::OPT_fobjc_gc_only); + if (!GCArg) + GCArg = Args.getLastArg(options::OPT_fobjc_gc); + if (GCArg) { + if (ARC) { + D.Diag(clang::diag::err_drv_objc_gc_arr) + << GCArg->getAsString(Args); + } else if (getToolChain().SupportsObjCGC()) { + GCArg->render(Args, CmdArgs); + } else { + // FIXME: We should move this to a hard error. + D.Diag(clang::diag::warn_drv_objc_gc_unsupported) + << GCArg->getAsString(Args); + } + } + + // Add exception args. + addExceptionArgs(Args, InputType, getToolChain().getTriple(), + KernelOrKext, IsRewriter, objcABIVersion, CmdArgs); + + if (getToolChain().UseSjLjExceptions()) + CmdArgs.push_back("-fsjlj-exceptions"); + + // C++ "sane" operator new. if (!Args.hasFlag(options::OPT_fassume_sane_operator_new, options::OPT_fno_assume_sane_operator_new)) CmdArgs.push_back("-fno-assume-sane-operator-new"); @@ -2858,6 +2957,17 @@ void darwin::Link::AddLinkArgs(Compilation &C, CmdArgs.push_back("-demangle"); } + // If we are using LTO, then automatically create a temporary file path for + // the linker to use, so that it's lifetime will extend past a possible + // dsymutil step. + if (Version[0] >= 116 && D.IsUsingLTO(Args)) { + const char *TmpPath = C.getArgs().MakeArgString( + D.GetTemporaryPath(types::getTypeTempSuffix(types::TY_Object))); + C.addTempFile(TmpPath); + CmdArgs.push_back("-object_path_lto"); + CmdArgs.push_back(TmpPath); + } + // Derived from the "link" spec. Args.AddAllArgs(CmdArgs, options::OPT_static); if (!Args.hasArg(options::OPT_static)) @@ -2921,6 +3031,7 @@ void darwin::Link::AddLinkArgs(Compilation &C, Args.AddLastArg(CmdArgs, options::OPT_dynamic); Args.AddAllArgs(CmdArgs, options::OPT_exported__symbols__list); Args.AddLastArg(CmdArgs, options::OPT_flat__namespace); + Args.AddAllArgs(CmdArgs, options::OPT_force__load); Args.AddAllArgs(CmdArgs, options::OPT_headerpad__max__install__names); Args.AddAllArgs(CmdArgs, options::OPT_image__base); Args.AddAllArgs(CmdArgs, options::OPT_init); @@ -3142,6 +3253,16 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, getDarwinToolChain().AddLinkSearchPathArgs(Args, CmdArgs); + // In ARC, if we don't have runtime support, link in the runtime + // stubs. We have to do this *before* adding any of the normal + // linker inputs so that its initializer gets run first. + if (isObjCAutoRefCount(Args)) { + ObjCRuntime runtime; + getDarwinToolChain().configureObjCRuntime(runtime); + if (!runtime.HasARC) + getDarwinToolChain().AddLinkARCArgs(Args, CmdArgs); + } + AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs); if (LinkingOutput) { @@ -3170,7 +3291,7 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA, // endfile_spec is empty. } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_F); @@ -3329,7 +3450,7 @@ void auroraux::Link::ConstructJob(Compilation &C, const JobAction &JA, getToolChain().GetFilePath("crtend.o"))); } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld")); @@ -3637,7 +3758,7 @@ void freebsd::Link::ConstructJob(Compilation &C, const JobAction &JA, "crtn.o"))); } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld")); @@ -3792,7 +3913,7 @@ void netbsd::Link::ConstructJob(Compilation &C, const JobAction &JA, "crtn.o"))); } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); const char *Exec = Args.MakeArgString(FindTargetProgramPath(getToolChain(), ToolTriple.getTriple(), @@ -4016,7 +4137,7 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, } } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); if (Args.hasArg(options::OPT_use_gold_plugin)) { CmdArgs.push_back("-plugin"); @@ -4100,7 +4221,7 @@ void minix::Link::ConstructJob(Compilation &C, const JobAction &JA, "/usr/gnu/lib/libend.a"))); } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("/usr/gnu/bin/gld")); @@ -4257,7 +4378,7 @@ void dragonfly::Link::ConstructJob(Compilation &C, const JobAction &JA, getToolChain().GetFilePath("crtn.o"))); } - addProfileRT(getToolChain(), Args, CmdArgs); + addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); const char *Exec = Args.MakeArgString(getToolChain().GetProgramPath("ld")); diff --git a/lib/Driver/Tools.h b/lib/Driver/Tools.h index 4a5a7e474526..1741d051ce9f 100644 --- a/lib/Driver/Tools.h +++ b/lib/Driver/Tools.h @@ -360,7 +360,7 @@ namespace netbsd { public: Link(const ToolChain &TC, const llvm::Triple &ToolTriple) - : Tool("netbsd::Ling", "linker", TC), ToolTriple(ToolTriple) {} + : Tool("netbsd::Link", "linker", TC), ToolTriple(ToolTriple) {} virtual bool hasIntegratedCPP() const { return false; } diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 8827116f2e40..5b0a52c65b84 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -102,7 +102,7 @@ ASTUnit::ASTUnit(bool _MainFileIsAST) ConcurrencyCheckValue(CheckUnlocked), PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), ShouldCacheCodeCompletionResults(false), - NestedMacroInstantiations(true), + NestedMacroExpansions(true), CompletionCacheTopLevelHashValue(0), PreambleTopLevelHashValue(0), CurrentTopLevelHashValue(0), @@ -182,6 +182,10 @@ static unsigned getDeclShowContexts(NamedDecl *ND, // all types are available due to functional casts. if (LangOpts.CPlusPlus || isa<ObjCInterfaceDecl>(ND)) Contexts |= (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)); + + // In Objective-C, you can only be a subclass of another Objective-C class + if (isa<ObjCInterfaceDecl>(ND)) + Contexts |= (1 << (CodeCompletionContext::CCC_ObjCSuperclass - 1)); // Deal with tag names. if (isa<EnumDecl>(ND)) { @@ -208,6 +212,8 @@ static unsigned getDeclShowContexts(NamedDecl *ND, | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)); } else if (isa<ObjCProtocolDecl>(ND)) { Contexts = (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1)); + } else if (isa<ObjCCategoryDecl>(ND)) { + Contexts = (1 << (CodeCompletionContext::CCC_ObjCCategoryName - 1)); } else if (isa<NamespaceDecl>(ND) || isa<NamespaceAliasDecl>(ND)) { Contexts = (1 << (CodeCompletionContext::CCC_Namespace - 1)); @@ -928,8 +934,8 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { // If the main file has been overridden due to the use of a preamble, // make that override happen and introduce the preamble. PreprocessorOptions &PreprocessorOpts = Clang->getPreprocessorOpts(); - PreprocessorOpts.DetailedRecordIncludesNestedMacroInstantiations - = NestedMacroInstantiations; + PreprocessorOpts.DetailedRecordIncludesNestedMacroExpansions + = NestedMacroExpansions; std::string PriorImplicitPCHInclude; if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); @@ -1150,16 +1156,19 @@ static llvm::MemoryBuffer *CreatePaddedMainFileBuffer(llvm::MemoryBuffer *Old, /// buffer that should be used in place of the main file when doing so. /// Otherwise, returns a NULL pointer. llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( - CompilerInvocation PreambleInvocation, + const CompilerInvocation &PreambleInvocationIn, bool AllowRebuild, unsigned MaxLines) { - FrontendOptions &FrontendOpts = PreambleInvocation.getFrontendOpts(); + + llvm::IntrusiveRefCntPtr<CompilerInvocation> + PreambleInvocation(new CompilerInvocation(PreambleInvocationIn)); + FrontendOptions &FrontendOpts = PreambleInvocation->getFrontendOpts(); PreprocessorOptions &PreprocessorOpts - = PreambleInvocation.getPreprocessorOpts(); + = PreambleInvocation->getPreprocessorOpts(); bool CreatedPreambleBuffer = false; std::pair<llvm::MemoryBuffer *, std::pair<unsigned, bool> > NewPreamble - = ComputePreamble(PreambleInvocation, MaxLines, CreatedPreambleBuffer); + = ComputePreamble(*PreambleInvocation, MaxLines, CreatedPreambleBuffer); // If ComputePreamble() Take ownership of the llvm::OwningPtr<llvm::MemoryBuffer> OwnedPreambleBuffer; @@ -1260,7 +1269,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // have occurred in the preamble. getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), - PreambleInvocation.getDiagnosticOpts()); + PreambleInvocation->getDiagnosticOpts()); getDiagnostics().setNumWarnings(NumWarningsInPreamble); if (StoredDiagnostics.size() > NumStoredDiagnosticsInPreamble) StoredDiagnostics.erase( @@ -1357,7 +1366,7 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( llvm::CrashRecoveryContextCleanupRegistrar<CompilerInstance> CICleanup(Clang.get()); - Clang->setInvocation(&PreambleInvocation); + Clang->setInvocation(&*PreambleInvocation); OriginalSourceFile = Clang->getFrontendOpts().Inputs[0].second; // Set up diagnostics, capturing all of the diagnostics produced. @@ -1716,7 +1725,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, bool PrecompilePreamble, bool CompleteTranslationUnit, bool CacheCodeCompletionResults, - bool NestedMacroInstantiations) { + bool NestedMacroExpansions) { // Create the AST unit. llvm::OwningPtr<ASTUnit> AST; AST.reset(new ASTUnit(false)); @@ -1727,7 +1736,7 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->CompleteTranslationUnit = CompleteTranslationUnit; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->Invocation = CI; - AST->NestedMacroInstantiations = NestedMacroInstantiations; + AST->NestedMacroExpansions = NestedMacroExpansions; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -1753,7 +1762,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, bool CacheCodeCompletionResults, bool CXXPrecompilePreamble, bool CXXChainedPCH, - bool NestedMacroInstantiations) { + bool NestedMacroExpansions) { if (!Diags.getPtr()) { // No diagnostics engine was provided, so create our own diagnostics object // with the default options. @@ -1820,7 +1829,7 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, AST->NumStoredDiagnosticsInPreamble = StoredDiagnostics.size(); AST->StoredDiagnostics.swap(StoredDiagnostics); AST->Invocation = CI; - AST->NestedMacroInstantiations = NestedMacroInstantiations; + AST->NestedMacroExpansions = NestedMacroExpansions; // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar<ASTUnit> @@ -1899,7 +1908,7 @@ namespace { /// results from an ASTUnit with the code-completion results provided to it, /// then passes the result on to class AugmentedCodeCompleteConsumer : public CodeCompleteConsumer { - unsigned NormalContexts; + unsigned long long NormalContexts; ASTUnit &AST; CodeCompleteConsumer &Next; @@ -1913,22 +1922,24 @@ namespace { // Compute the set of contexts in which we will look when we don't have // any information about the specific context. NormalContexts - = (1 << (CodeCompletionContext::CCC_TopLevel - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCInterface - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCImplementation - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCIvarList - 1)) - | (1 << (CodeCompletionContext::CCC_Statement - 1)) - | (1 << (CodeCompletionContext::CCC_Expression - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) - | (1 << (CodeCompletionContext::CCC_MemberAccess - 1)) - | (1 << (CodeCompletionContext::CCC_ObjCProtocolName - 1)) - | (1 << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)) - | (1 << (CodeCompletionContext::CCC_Recovery - 1)); + = (1LL << (CodeCompletionContext::CCC_TopLevel - 1)) + | (1LL << (CodeCompletionContext::CCC_ObjCInterface - 1)) + | (1LL << (CodeCompletionContext::CCC_ObjCImplementation - 1)) + | (1LL << (CodeCompletionContext::CCC_ObjCIvarList - 1)) + | (1LL << (CodeCompletionContext::CCC_Statement - 1)) + | (1LL << (CodeCompletionContext::CCC_Expression - 1)) + | (1LL << (CodeCompletionContext::CCC_ObjCMessageReceiver - 1)) + | (1LL << (CodeCompletionContext::CCC_DotMemberAccess - 1)) + | (1LL << (CodeCompletionContext::CCC_ArrowMemberAccess - 1)) + | (1LL << (CodeCompletionContext::CCC_ObjCPropertyAccess - 1)) + | (1LL << (CodeCompletionContext::CCC_ObjCProtocolName - 1)) + | (1LL << (CodeCompletionContext::CCC_ParenthesizedExpression - 1)) + | (1LL << (CodeCompletionContext::CCC_Recovery - 1)); if (AST.getASTContext().getLangOptions().CPlusPlus) - NormalContexts |= (1 << (CodeCompletionContext::CCC_EnumTag - 1)) - | (1 << (CodeCompletionContext::CCC_UnionTag - 1)) - | (1 << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); + NormalContexts |= (1LL << (CodeCompletionContext::CCC_EnumTag - 1)) + | (1LL << (CodeCompletionContext::CCC_UnionTag - 1)) + | (1LL << (CodeCompletionContext::CCC_ClassOrStructTag - 1)); } virtual void ProcessCodeCompleteResults(Sema &S, @@ -1966,12 +1977,15 @@ static void CalculateHiddenNames(const CodeCompletionContext &Context, case CodeCompletionContext::CCC_Statement: case CodeCompletionContext::CCC_Expression: case CodeCompletionContext::CCC_ObjCMessageReceiver: - case CodeCompletionContext::CCC_MemberAccess: + case CodeCompletionContext::CCC_DotMemberAccess: + case CodeCompletionContext::CCC_ArrowMemberAccess: + case CodeCompletionContext::CCC_ObjCPropertyAccess: case CodeCompletionContext::CCC_Namespace: case CodeCompletionContext::CCC_Type: case CodeCompletionContext::CCC_Name: case CodeCompletionContext::CCC_PotentiallyQualifiedName: case CodeCompletionContext::CCC_ParenthesizedExpression: + case CodeCompletionContext::CCC_ObjCSuperclass: break; case CodeCompletionContext::CCC_EnumTag: @@ -1990,6 +2004,9 @@ static void CalculateHiddenNames(const CodeCompletionContext &Context, case CodeCompletionContext::CCC_TypeQualifiers: case CodeCompletionContext::CCC_Other: case CodeCompletionContext::CCC_OtherWithMacros: + case CodeCompletionContext::CCC_ObjCInstanceMessage: + case CodeCompletionContext::CCC_ObjCClassMessage: + case CodeCompletionContext::CCC_ObjCCategoryName: // We're looking for nothing, or we're looking for names that cannot // be hidden. return; @@ -2284,9 +2301,9 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, } } -bool ASTUnit::Save(llvm::StringRef File) { - if (getDiagnostics().hasErrorOccurred()) - return true; +CXSaveError ASTUnit::Save(llvm::StringRef File) { + if (getDiagnostics().hasUnrecoverableErrorOccurred()) + return CXSaveError_TranslationErrors; // FIXME: Can we somehow regenerate the stat cache here, or do we need to // unconditionally create a stat cache when we parse the file? @@ -2294,11 +2311,11 @@ bool ASTUnit::Save(llvm::StringRef File) { llvm::raw_fd_ostream Out(File.str().c_str(), ErrorInfo, llvm::raw_fd_ostream::F_Binary); if (!ErrorInfo.empty() || Out.has_error()) - return true; + return CXSaveError_Unknown; serialize(Out); Out.close(); - return Out.has_error(); + return Out.has_error()? CXSaveError_Unknown : CXSaveError_None; } bool ASTUnit::serialize(llvm::raw_ostream &OS) { diff --git a/lib/Frontend/BoostConAction.cpp b/lib/Frontend/BoostConAction.cpp deleted file mode 100644 index 4a12ff2ebc95..000000000000 --- a/lib/Frontend/BoostConAction.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//===-- BoostConAction.cpp - BoostCon Workshop Action -----------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -#include "clang/Frontend/FrontendActions.h" -#include "clang/AST/ASTConsumer.h" -#include "clang/AST/RecursiveASTVisitor.h" -#include <cstdio> -#include <iostream> -using namespace clang; - -namespace { - class BoostConASTConsumer : public ASTConsumer, - public RecursiveASTVisitor<BoostConASTConsumer> { - public: - /// HandleTranslationUnit - This method is called when the ASTs for entire - /// translation unit have been parsed. - virtual void HandleTranslationUnit(ASTContext &Ctx); - - bool VisitCXXRecordDecl(CXXRecordDecl *D) { - std::cout << D->getNameAsString() << std::endl; - return true; - } - }; -} - -ASTConsumer *BoostConAction::CreateASTConsumer(CompilerInstance &CI, - llvm::StringRef InFile) { - return new BoostConASTConsumer(); -} - -void BoostConASTConsumer::HandleTranslationUnit(ASTContext &Ctx) { - fprintf(stderr, "Welcome to BoostCon!\n"); - TraverseDecl(Ctx.getTranslationUnitDecl()); -} diff --git a/lib/Frontend/CMakeLists.txt b/lib/Frontend/CMakeLists.txt index 7dcbebff39b8..556b1333c19b 100644 --- a/lib/Frontend/CMakeLists.txt +++ b/lib/Frontend/CMakeLists.txt @@ -12,7 +12,6 @@ add_clang_library(clangFrontend ASTConsumers.cpp ASTMerge.cpp ASTUnit.cpp - BoostConAction.cpp CacheTokens.cpp CompilerInstance.cpp CompilerInvocation.cpp diff --git a/lib/Frontend/CacheTokens.cpp b/lib/Frontend/CacheTokens.cpp index 06a1fd29838b..20b51893fcc2 100644 --- a/lib/Frontend/CacheTokens.cpp +++ b/lib/Frontend/CacheTokens.cpp @@ -247,17 +247,16 @@ void PTHWriter::EmitToken(const Token& T) { } else { // We cache *un-cleaned* spellings. This gives us 100% fidelity with the // source code. - const char* s = T.getLiteralData(); - unsigned len = T.getLength(); + llvm::StringRef s(T.getLiteralData(), T.getLength()); // Get the string entry. - llvm::StringMapEntry<OffsetOpt> *E = &CachedStrs.GetOrCreateValue(s, s+len); + llvm::StringMapEntry<OffsetOpt> *E = &CachedStrs.GetOrCreateValue(s); // If this is a new string entry, bump the PTH offset. if (!E->getValue().hasOffset()) { E->getValue().setOffset(CurStrOffset); StrEntries.push_back(E); - CurStrOffset += len + 1; + CurStrOffset += s.size() + 1; } // Emit the relative offset into the PTH file for the spelling string. diff --git a/lib/Frontend/CompilerInstance.cpp b/lib/Frontend/CompilerInstance.cpp index 38fcfe3c4711..c58e3af5089b 100644 --- a/lib/Frontend/CompilerInstance.cpp +++ b/lib/Frontend/CompilerInstance.cpp @@ -38,6 +38,7 @@ #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/system_error.h" +#include "llvm/Config/config.h" using namespace clang; CompilerInstance::CompilerInstance() @@ -228,7 +229,7 @@ CompilerInstance::createPreprocessor(Diagnostic &Diags, if (PPOpts.DetailedRecord) PP->createPreprocessingRecord( - PPOpts.DetailedRecordIncludesNestedMacroInstantiations); + PPOpts.DetailedRecordIncludesNestedMacroExpansions); InitializePreprocessor(*PP, PPOpts, HSOpts, FEOpts); diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 536512a121e9..06b72602fd18 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -123,6 +123,10 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-dwarf-debug-flags"); Res.push_back(Opts.DwarfDebugFlags); } + if (Opts.ObjCRuntimeHasARC) + Res.push_back("-fobjc-runtime-has-arc"); + if (Opts.ObjCRuntimeHasTerminate) + Res.push_back("-fobjc-runtime-has-terminate"); if (Opts.EmitGcovArcs) Res.push_back("-femit-coverage-data"); if (Opts.EmitGcovNotes) @@ -133,6 +137,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-fno-common"); if (Opts.ForbidGuardVariables) Res.push_back("-fforbid-guard-variables"); + if (Opts.UseRegisterSizedBitfieldAccess) + Res.push_back("-fuse-register-sized-bitfield-access"); if (Opts.NoImplicitFloat) Res.push_back("-no-implicit-float"); if (Opts.OmitLeafFramePointer) @@ -169,6 +175,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-fno-use-cxa-atexit"); if (Opts.CXXCtorDtorAliases) Res.push_back("-mconstructor-aliases"); + if (Opts.ObjCAutoRefCountExceptions) + Res.push_back("-fobjc-arc-eh"); if (!Opts.DebugPass.empty()) { Res.push_back("-mdebug-pass"); Res.push_back(Opts.DebugPass); @@ -199,6 +207,8 @@ static void CodeGenOptsToArgs(const CodeGenOptions &Opts, Res.push_back("-mregparm"); Res.push_back(llvm::utostr(Opts.NumRegisterParameters)); } + if (Opts.NoExecStack) + Res.push_back("-mnoexecstack"); if (Opts.RelaxAll) Res.push_back("-mrelax-all"); if (Opts.SaveTempLabels) @@ -351,7 +361,6 @@ static const char *getActionName(frontend::ActionKind Kind) { case frontend::ASTDumpXML: return "-ast-dump-xml"; case frontend::ASTPrint: return "-ast-print"; case frontend::ASTView: return "-ast-view"; - case frontend::BoostCon: return "-boostcon"; case frontend::CreateModule: return "-create-module"; case frontend::DumpRawTokens: return "-dump-raw-tokens"; case frontend::DumpTokens: return "-dump-tokens"; @@ -413,6 +422,23 @@ static void FrontendOptsToArgs(const FrontendOptions &Opts, Res.push_back("-version"); if (Opts.FixWhatYouCan) Res.push_back("-fix-what-you-can"); + switch (Opts.ARCMTAction) { + case FrontendOptions::ARCMT_None: + break; + case FrontendOptions::ARCMT_Check: + Res.push_back("-arcmt-check"); + break; + case FrontendOptions::ARCMT_Modify: + Res.push_back("-arcmt-modify"); + break; + case FrontendOptions::ARCMT_Migrate: + Res.push_back("-arcmt-migrate"); + break; + } + if (!Opts.ARCMTMigrateDir.empty()) { + Res.push_back("-arcmt-migrate-directory"); + Res.push_back(Opts.ARCMTMigrateDir); + } bool NeedLang = false; for (unsigned i = 0, e = Opts.Inputs.size(); i != e; ++i) @@ -537,6 +563,8 @@ static void HeaderSearchOptsToArgs(const HeaderSearchOptions &Opts, Res.push_back("-nostdinc"); if (!Opts.UseStandardCXXIncludes) Res.push_back("-nostdinc++"); + if (Opts.UseLibcxx) + Res.push_back("-stdlib=libc++"); if (Opts.Verbose) Res.push_back("-v"); } @@ -670,8 +698,12 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-fobjc-gc-only"); } } - if (Opts.ObjCInferRelatedResultType) - Res.push_back("-fobjc-infer-related-result-type"); + if (Opts.ObjCAutoRefCount) + Res.push_back("-fobjc-arc"); + if (Opts.ObjCRuntimeHasWeak) + Res.push_back("-fobjc-runtime-has-weak"); + if (!Opts.ObjCInferRelatedResultType) + Res.push_back("-fno-objc-infer-related-result-type"); if (Opts.AppleKext) Res.push_back("-fapple-kext"); @@ -705,6 +737,8 @@ static void LangOptsToArgs(const LangOptions &Opts, Res.push_back("-ffake-address-space-map"); if (Opts.ParseUnknownAnytype) Res.push_back("-funknown-anytype"); + if (Opts.DebuggerSupport) + Res.push_back("-fdebugger-support"); if (Opts.DelayedTemplateParsing) Res.push_back("-fdelayed-template-parsing"); if (Opts.Deprecated) @@ -938,6 +972,8 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.DisableLLVMOpts = Args.hasArg(OPT_disable_llvm_optzns); Opts.DisableRedZone = Args.hasArg(OPT_disable_red_zone); Opts.ForbidGuardVariables = Args.hasArg(OPT_fforbid_guard_variables); + Opts.UseRegisterSizedBitfieldAccess = Args.hasArg( + OPT_fuse_register_sized_bitfield_access); Opts.RelaxedAliasing = Args.hasArg(OPT_relaxed_aliasing); Opts.DwarfDebugFlags = Args.getLastArgValue(OPT_dwarf_debug_flags); Opts.MergeAllConstants = !Args.hasArg(OPT_fno_merge_all_constants); @@ -951,6 +987,9 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, (Opts.OptimizationLevel > 1 && !Opts.OptimizeSize); Opts.AsmVerbose = Args.hasArg(OPT_masm_verbose); + Opts.ObjCAutoRefCountExceptions = Args.hasArg(OPT_fobjc_arc_exceptions); + Opts.ObjCRuntimeHasARC = Args.hasArg(OPT_fobjc_runtime_has_arc); + Opts.ObjCRuntimeHasTerminate = Args.hasArg(OPT_fobjc_runtime_has_terminate); Opts.CXAAtExit = !Args.hasArg(OPT_fno_use_cxa_atexit); Opts.CXXCtorDtorAliases = Args.hasArg(OPT_mconstructor_aliases); Opts.CodeModel = Args.getLastArgValue(OPT_mcode_model); @@ -965,6 +1004,7 @@ static void ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK, Opts.NoZeroInitializedInBSS = Args.hasArg(OPT_mno_zero_initialized_in_bss); Opts.BackendOptions = Args.getAllArgValues(OPT_backend_option); Opts.NumRegisterParameters = Args.getLastArgIntValue(OPT_mregparm, 0, Diags); + Opts.NoExecStack = Args.hasArg(OPT_mno_exec_stack); Opts.RelaxAll = Args.hasArg(OPT_mrelax_all); Opts.OmitLeafFramePointer = Args.hasArg(OPT_momit_leaf_frame_pointer); Opts.SaveTempLabels = Args.hasArg(OPT_msave_temp_labels); @@ -1010,6 +1050,7 @@ static void ParseDependencyOutputArgs(DependencyOutputOptions &Opts, Opts.UsePhonyTargets = Args.hasArg(OPT_MP); Opts.ShowHeaderIncludes = Args.hasArg(OPT_H); Opts.HeaderIncludeOutputFile = Args.getLastArgValue(OPT_header_include_file); + Opts.AddMissingHeaderDeps = Args.hasArg(OPT_MG); } static void ParseDiagnosticArgs(DiagnosticOptions &Opts, ArgList &Args, @@ -1117,8 +1158,6 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.ProgramAction = frontend::ASTPrint; break; case OPT_ast_view: Opts.ProgramAction = frontend::ASTView; break; - case OPT_boostcon: - Opts.ProgramAction = frontend::BoostCon; break; case OPT_dump_raw_tokens: Opts.ProgramAction = frontend::DumpRawTokens; break; case OPT_dump_tokens: @@ -1220,6 +1259,26 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args, Opts.FixWhatYouCan = Args.hasArg(OPT_fix_what_you_can); Opts.Modules = Args.getAllArgValues(OPT_import_module); + Opts.ARCMTAction = FrontendOptions::ARCMT_None; + if (const Arg *A = Args.getLastArg(OPT_arcmt_check, + OPT_arcmt_modify, + OPT_arcmt_migrate)) { + switch (A->getOption().getID()) { + default: + llvm_unreachable("missed a case"); + case OPT_arcmt_check: + Opts.ARCMTAction = FrontendOptions::ARCMT_Check; + break; + case OPT_arcmt_modify: + Opts.ARCMTAction = FrontendOptions::ARCMT_Modify; + break; + case OPT_arcmt_migrate: + Opts.ARCMTAction = FrontendOptions::ARCMT_Migrate; + break; + } + } + Opts.ARCMTMigrateDir = Args.getLastArgValue(OPT_arcmt_migrate_directory); + InputKind DashX = IK_None; if (const Arg *A = Args.getLastArg(OPT_x)) { DashX = llvm::StringSwitch<InputKind>(A->getValue(Args)) @@ -1291,13 +1350,15 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Opts.UseBuiltinIncludes = !Args.hasArg(OPT_nobuiltininc); Opts.UseStandardIncludes = !Args.hasArg(OPT_nostdinc); Opts.UseStandardCXXIncludes = !Args.hasArg(OPT_nostdincxx); + if (const Arg *A = Args.getLastArg(OPT_stdlib_EQ)) + Opts.UseLibcxx = (strcmp(A->getValue(Args), "libc++") == 0); Opts.ResourceDir = Args.getLastArgValue(OPT_resource_dir); // Add -I... and -F... options in order. for (arg_iterator it = Args.filtered_begin(OPT_I, OPT_F), ie = Args.filtered_end(); it != ie; ++it) Opts.AddPath((*it)->getValue(Args), frontend::Angled, true, - /*IsFramework=*/ (*it)->getOption().matches(OPT_F), true); + /*IsFramework=*/ (*it)->getOption().matches(OPT_F), false); // Add -iprefix/-iwith-prefix/-iwithprefixbefore options. llvm::StringRef Prefix = ""; // FIXME: This isn't the correct default prefix. @@ -1309,24 +1370,24 @@ static void ParseHeaderSearchArgs(HeaderSearchOptions &Opts, ArgList &Args) { Prefix = A->getValue(Args); else if (A->getOption().matches(OPT_iwithprefix)) Opts.AddPath(Prefix.str() + A->getValue(Args), - frontend::System, false, false, true); + frontend::System, false, false, false); else Opts.AddPath(Prefix.str() + A->getValue(Args), - frontend::Angled, false, false, true); + frontend::Angled, false, false, false); } for (arg_iterator it = Args.filtered_begin(OPT_idirafter), ie = Args.filtered_end(); it != ie; ++it) - Opts.AddPath((*it)->getValue(Args), frontend::After, true, false, true); + Opts.AddPath((*it)->getValue(Args), frontend::After, true, false, false); for (arg_iterator it = Args.filtered_begin(OPT_iquote), ie = Args.filtered_end(); it != ie; ++it) - Opts.AddPath((*it)->getValue(Args), frontend::Quoted, true, false, true); + Opts.AddPath((*it)->getValue(Args), frontend::Quoted, true, false, false); for (arg_iterator it = Args.filtered_begin(OPT_cxx_isystem, OPT_isystem, OPT_iwithsysroot), ie = Args.filtered_end(); it != ie; ++it) Opts.AddPath((*it)->getValue(Args), ((*it)->getOption().matches(OPT_cxx_isystem) ? frontend::CXXSystem : frontend::System), - true, false, (*it)->getOption().matches(OPT_iwithsysroot)); + true, false, !(*it)->getOption().matches(OPT_iwithsysroot)); // FIXME: Need options for the various environment variables! } @@ -1480,17 +1541,27 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, if (Args.hasArg(OPT_fno_operator_names)) Opts.CXXOperatorNames = 0; + if (Opts.ObjC1) { + if (Args.hasArg(OPT_fobjc_gc_only)) + Opts.setGCMode(LangOptions::GCOnly); + else if (Args.hasArg(OPT_fobjc_gc)) + Opts.setGCMode(LangOptions::HybridGC); + else if (Args.hasArg(OPT_fobjc_arc)) { + Opts.ObjCAutoRefCount = 1; + if (!Args.hasArg(OPT_fobjc_nonfragile_abi)) + Diags.Report(diag::err_arc_nonfragile_abi); + } + + if (Args.hasArg(OPT_fobjc_runtime_has_weak)) + Opts.ObjCRuntimeHasWeak = 1; + + if (Args.hasArg(OPT_fno_objc_infer_related_result_type)) + Opts.ObjCInferRelatedResultType = 0; + } + if (Args.hasArg(OPT_fgnu89_inline)) Opts.GNUInline = 1; - if (Args.hasArg(OPT_fobjc_gc_only)) - Opts.setGCMode(LangOptions::GCOnly); - else if (Args.hasArg(OPT_fobjc_gc)) - Opts.setGCMode(LangOptions::HybridGC); - - if (Args.hasArg(OPT_fobjc_infer_related_result_type)) - Opts.ObjCInferRelatedResultType = 1; - if (Args.hasArg(OPT_fapple_kext)) { if (!Opts.CPlusPlus) Diags.Report(diag::warn_c_kext); @@ -1598,6 +1669,7 @@ static void ParseLangArgs(LangOptions &Opts, ArgList &Args, InputKind IK, Opts.MRTD = Args.hasArg(OPT_mrtd); Opts.FakeAddressSpaceMap = Args.hasArg(OPT_ffake_address_space_map); Opts.ParseUnknownAnytype = Args.hasArg(OPT_funknown_anytype); + Opts.DebuggerSupport = Args.hasArg(OPT_fdebugger_support); // Record whether the __DEPRECATED define was requested. Opts.Deprecated = Args.hasFlag(OPT_fdeprecated_macro, @@ -1715,6 +1787,19 @@ static void ParsePreprocessorArgs(PreprocessorOptions &Opts, ArgList &Args, Opts.addRemappedFile(Split.first, Split.second); } + + if (Arg *A = Args.getLastArg(OPT_fobjc_arc_cxxlib_EQ)) { + llvm::StringRef Name = A->getValue(Args); + unsigned Library = llvm::StringSwitch<unsigned>(Name) + .Case("libc++", ARCXX_libcxx) + .Case("libstdc++", ARCXX_libstdcxx) + .Case("none", ARCXX_nolib) + .Default(~0U); + if (Library == ~0U) + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Name; + else + Opts.ObjCXXARCStandardLibrary = (ObjCXXARCStandardLibraryKind)Library; + } } static void ParsePreprocessorOutputArgs(PreprocessorOutputOptions &Opts, diff --git a/lib/Frontend/DependencyFile.cpp b/lib/Frontend/DependencyFile.cpp index 5c3a23128a14..1edd09b06c4c 100644 --- a/lib/Frontend/DependencyFile.cpp +++ b/lib/Frontend/DependencyFile.cpp @@ -17,9 +17,11 @@ #include "clang/Frontend/DependencyOutputOptions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Lex/DirectoryLookup.h" +#include "clang/Lex/LexDiagnostic.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/StringSet.h" +#include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -33,9 +35,11 @@ class DependencyFileCallback : public PPCallbacks { llvm::raw_ostream *OS; bool IncludeSystemHeaders; bool PhonyTarget; + bool AddMissingHeaderDeps; private: bool FileMatchesDepCriteria(const char *Filename, SrcMgr::CharacteristicKind FileType); + void AddFilename(llvm::StringRef Filename); void OutputDependencyFile(); public: @@ -44,10 +48,19 @@ public: const DependencyOutputOptions &Opts) : PP(_PP), Targets(Opts.Targets), OS(_OS), IncludeSystemHeaders(Opts.IncludeSystemHeaders), - PhonyTarget(Opts.UsePhonyTargets) {} + PhonyTarget(Opts.UsePhonyTargets), + AddMissingHeaderDeps(Opts.AddMissingHeaderDeps) {} virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, SrcMgr::CharacteristicKind FileType); + virtual void InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + llvm::StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + llvm::StringRef SearchPath, + llvm::StringRef RelativePath); virtual void EndOfMainFile() { OutputDependencyFile(); @@ -72,6 +85,16 @@ void clang::AttachDependencyFileGen(Preprocessor &PP, return; } + // Disable the "file not found" diagnostic if the -MG option was given. + // FIXME: Ideally this would live in the driver, but we don't have the ability + // to remap individual diagnostics there without creating a DiagGroup, in + // which case we would need to prevent the group name from showing up in + // diagnostics. + if (Opts.AddMissingHeaderDeps) { + PP.getDiagnostics().setDiagnosticMapping(diag::warn_pp_file_not_found, + diag::MAP_IGNORE, SourceLocation()); + } + PP.addPPCallbacks(new DependencyFileCallback(&PP, OS, Opts)); } @@ -103,14 +126,34 @@ void DependencyFileCallback::FileChanged(SourceLocation Loc, SM.getFileEntryForID(SM.getFileID(SM.getInstantiationLoc(Loc))); if (FE == 0) return; - const char *Filename = FE->getName(); - if (!FileMatchesDepCriteria(Filename, FileType)) + llvm::StringRef Filename = FE->getName(); + if (!FileMatchesDepCriteria(Filename.data(), FileType)) return; - // Remove leading "./" - if (Filename[0] == '.' && Filename[1] == '/') - Filename = &Filename[2]; + // Remove leading "./" (or ".//" or "././" etc.) + while (Filename.size() > 2 && Filename[0] == '.' && + llvm::sys::path::is_separator(Filename[1])) { + Filename = Filename.substr(1); + while (llvm::sys::path::is_separator(Filename[0])) + Filename = Filename.substr(1); + } + + AddFilename(Filename); +} + +void DependencyFileCallback::InclusionDirective(SourceLocation HashLoc, + const Token &IncludeTok, + llvm::StringRef FileName, + bool IsAngled, + const FileEntry *File, + SourceLocation EndLoc, + llvm::StringRef SearchPath, + llvm::StringRef RelativePath) { + if (AddMissingHeaderDeps && !File) + AddFilename(FileName); +} +void DependencyFileCallback::AddFilename(llvm::StringRef Filename) { if (FilesSet.insert(Filename)) Files.push_back(Filename); } diff --git a/lib/Frontend/FrontendAction.cpp b/lib/Frontend/FrontendAction.cpp index 42da44c2c720..0128d6ee0531 100644 --- a/lib/Frontend/FrontendAction.cpp +++ b/lib/Frontend/FrontendAction.cpp @@ -130,6 +130,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI, setCurrentFile(Filename, InputKind); setCompilerInstance(&CI); + if (!BeginInvocation(CI)) + goto failure; + // AST files follow a very different path, since they share objects via the // AST unit. if (InputKind == IK_AST) { @@ -381,3 +384,46 @@ PreprocessorFrontendAction::CreateASTConsumer(CompilerInstance &CI, llvm::StringRef InFile) { llvm_unreachable("Invalid CreateASTConsumer on preprocessor action!"); } + +ASTConsumer *WrapperFrontendAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return WrappedAction->CreateASTConsumer(CI, InFile); +} +bool WrapperFrontendAction::BeginInvocation(CompilerInstance &CI) { + return WrappedAction->BeginInvocation(CI); +} +bool WrapperFrontendAction::BeginSourceFileAction(CompilerInstance &CI, + llvm::StringRef Filename) { + WrappedAction->setCurrentFile(getCurrentFile(), getCurrentFileKind()); + WrappedAction->setCompilerInstance(&CI); + return WrappedAction->BeginSourceFileAction(CI, Filename); +} +void WrapperFrontendAction::ExecuteAction() { + WrappedAction->ExecuteAction(); +} +void WrapperFrontendAction::EndSourceFileAction() { + WrappedAction->EndSourceFileAction(); +} + +bool WrapperFrontendAction::usesPreprocessorOnly() const { + return WrappedAction->usesPreprocessorOnly(); +} +bool WrapperFrontendAction::usesCompleteTranslationUnit() { + return WrappedAction->usesCompleteTranslationUnit(); +} +bool WrapperFrontendAction::hasPCHSupport() const { + return WrappedAction->hasPCHSupport(); +} +bool WrapperFrontendAction::hasASTFileSupport() const { + return WrappedAction->hasASTFileSupport(); +} +bool WrapperFrontendAction::hasIRSupport() const { + return WrappedAction->hasIRSupport(); +} +bool WrapperFrontendAction::hasCodeCompletionSupport() const { + return WrappedAction->hasCodeCompletionSupport(); +} + +WrapperFrontendAction::WrapperFrontendAction(FrontendAction *WrappedAction) + : WrappedAction(WrappedAction) {} + diff --git a/lib/Frontend/InitHeaderSearch.cpp b/lib/Frontend/InitHeaderSearch.cpp index ad931166b8fc..e11a415db194 100644 --- a/lib/Frontend/InitHeaderSearch.cpp +++ b/lib/Frontend/InitHeaderSearch.cpp @@ -45,7 +45,7 @@ class InitHeaderSearch { std::vector<std::pair<IncludeDirGroup, DirectoryLookup> > IncludePath; typedef std::vector<std::pair<IncludeDirGroup, DirectoryLookup> >::const_iterator path_iterator; - HeaderSearch& Headers; + HeaderSearch &Headers; bool Verbose; std::string IncludeSysroot; bool IsNotEmptyOrRoot; @@ -78,7 +78,8 @@ public: /// AddMinGW64CXXPaths - Add the necessary paths to support /// libstdc++ of x86_64-w64-mingw32 aka mingw-w64. - void AddMinGW64CXXPaths(llvm::StringRef Base); + void AddMinGW64CXXPaths(llvm::StringRef Base, + llvm::StringRef Version); /// AddDelimitedPaths - Add a list of paths delimited by the system PATH /// separator. The processing follows that of the CPATH variable for gcc. @@ -90,7 +91,8 @@ public: // AddDefaultCPlusPlusIncludePaths - Add paths that should be searched when // compiling c++. - void AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple); + void AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, + const HeaderSearchOptions &HSOpts); /// AddDefaultSystemIncludePaths - Adds the default system include paths so /// that e.g. stdio.h is found. @@ -103,7 +105,7 @@ public: void Realize(const LangOptions &Lang); }; -} +} // end anonymous namespace. void InitHeaderSearch::AddPath(const llvm::Twine &Path, IncludeDirGroup Group, bool isCXXAware, @@ -216,12 +218,16 @@ void InitHeaderSearch::AddMinGWCPlusPlusIncludePaths(llvm::StringRef Base, CXXSystem, true, false, false); } -void InitHeaderSearch::AddMinGW64CXXPaths(llvm::StringRef Base) { - AddPath(Base, +void InitHeaderSearch::AddMinGW64CXXPaths(llvm::StringRef Base, + llvm::StringRef Version) { + // Assumes Base is HeaderSearchOpts' ResourceDir + AddPath(Base + "/../../../include/c++/" + Version, + CXXSystem, true, false, false); + AddPath(Base + "/../../../include/c++/" + Version + "/x86_64-w64-mingw32", CXXSystem, true, false, false); - AddPath(Base + "/x86_64-w64-mingw32", + AddPath(Base + "/../../../include/c++/" + Version + "/i686-w64-mingw32", CXXSystem, true, false, false); - AddPath(Base + "/backward", + AddPath(Base + "/../../../include/c++/" + Version + "/backward", CXXSystem, true, false, false); } @@ -245,24 +251,23 @@ static bool getSystemRegistryString(const char *keyPath, const char *valueName, DWORD valueSize = maxLength - 1; long lResult; bool returnValue = false; + if (strncmp(keyPath, "HKEY_CLASSES_ROOT\\", 18) == 0) { hRootKey = HKEY_CLASSES_ROOT; subKey = keyPath + 18; - } - else if (strncmp(keyPath, "HKEY_USERS\\", 11) == 0) { + } else if (strncmp(keyPath, "HKEY_USERS\\", 11) == 0) { hRootKey = HKEY_USERS; subKey = keyPath + 11; - } - else if (strncmp(keyPath, "HKEY_LOCAL_MACHINE\\", 19) == 0) { + } else if (strncmp(keyPath, "HKEY_LOCAL_MACHINE\\", 19) == 0) { hRootKey = HKEY_LOCAL_MACHINE; subKey = keyPath + 19; - } - else if (strncmp(keyPath, "HKEY_CURRENT_USER\\", 18) == 0) { + } else if (strncmp(keyPath, "HKEY_CURRENT_USER\\", 18) == 0) { hRootKey = HKEY_CURRENT_USER; subKey = keyPath + 18; } else - return(false); + return false; + const char *placeHolder = strstr(subKey, "$VERSION"); char bestName[256]; bestName[0] = '\0'; @@ -338,7 +343,7 @@ static bool getSystemRegistryString(const char *keyPath, const char *valueName, RegCloseKey(hKey); } } - return(returnValue); + return returnValue; } #else // _MSC_VER // Read registry string. @@ -351,12 +356,12 @@ static bool getSystemRegistryString(const char*, const char*, char*, size_t) { static bool getVisualStudioDir(std::string &path) { // First check the environment variables that vsvars32.bat sets. const char* vcinstalldir = getenv("VCINSTALLDIR"); - if(vcinstalldir) { + if (vcinstalldir) { char *p = const_cast<char *>(strstr(vcinstalldir, "\\VC")); if (p) *p = '\0'; path = vcinstalldir; - return(true); + return true; } char vsIDEInstallDir[256]; @@ -374,56 +379,52 @@ static bool getVisualStudioDir(std::string &path) { if (p) *p = '\0'; path = vsIDEInstallDir; - return(true); + return true; } - else if (hasVCExpressDir && vsExpressIDEInstallDir[0]) { + + if (hasVCExpressDir && vsExpressIDEInstallDir[0]) { char *p = (char*)strstr(vsExpressIDEInstallDir, "\\Common7\\IDE"); if (p) *p = '\0'; path = vsExpressIDEInstallDir; - return(true); + return true; } - else { - // Try the environment. - const char* vs100comntools = getenv("VS100COMNTOOLS"); - const char* vs90comntools = getenv("VS90COMNTOOLS"); - const char* vs80comntools = getenv("VS80COMNTOOLS"); - const char* vscomntools = NULL; - - // Try to find the version that we were compiled with - if(false) {} - #if (_MSC_VER >= 1600) // VC100 - else if(vs100comntools) { - vscomntools = vs100comntools; - } - #elif (_MSC_VER == 1500) // VC80 - else if(vs90comntools) { - vscomntools = vs90comntools; - } - #elif (_MSC_VER == 1400) // VC80 - else if(vs80comntools) { - vscomntools = vs80comntools; - } - #endif - // Otherwise find any version we can - else if (vs100comntools) - vscomntools = vs100comntools; - else if (vs90comntools) - vscomntools = vs90comntools; - else if (vs80comntools) - vscomntools = vs80comntools; - - if (vscomntools && *vscomntools) { - char *p = const_cast<char *>(strstr(vscomntools, "\\Common7\\Tools")); - if (p) - *p = '\0'; - path = vscomntools; - return(true); - } - else - return(false); + + // Try the environment. + const char *vs100comntools = getenv("VS100COMNTOOLS"); + const char *vs90comntools = getenv("VS90COMNTOOLS"); + const char *vs80comntools = getenv("VS80COMNTOOLS"); + const char *vscomntools = NULL; + + // Try to find the version that we were compiled with + if(false) {} + #if (_MSC_VER >= 1600) // VC100 + else if(vs100comntools) { + vscomntools = vs100comntools; } - return(false); + #elif (_MSC_VER == 1500) // VC80 + else if(vs90comntools) { + vscomntools = vs90comntools; + } + #elif (_MSC_VER == 1400) // VC80 + else if(vs80comntools) { + vscomntools = vs80comntools; + } + #endif + // Otherwise find any version we can + else if (vs100comntools) + vscomntools = vs100comntools; + else if (vs90comntools) + vscomntools = vs90comntools; + else if (vs80comntools) + vscomntools = vs80comntools; + + if (vscomntools && *vscomntools) { + const char *p = strstr(vscomntools, "\\Common7\\Tools"); + path = p ? std::string(vscomntools, p) : vscomntools; + return true; + } + return false; } // Get Windows SDK installation directory. @@ -432,7 +433,9 @@ static bool getWindowsSDKDir(std::string &path) { // Try the Windows registry. bool hasSDKDir = getSystemRegistryString( "HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\$VERSION", - "InstallationFolder", windowsSDKInstallDir, sizeof(windowsSDKInstallDir) - 1); + "InstallationFolder", + windowsSDKInstallDir, + sizeof(windowsSDKInstallDir) - 1); // If we have both vc80 and vc90, pick version we were compiled with. if (hasSDKDir && windowsSDKInstallDir[0]) { path = windowsSDKInstallDir; @@ -548,17 +551,28 @@ void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, AddPath("/boot/develop/headers/posix", System, true, false, false); AddPath("/boot/develop/headers", System, true, false, false); break; + case llvm::Triple::RTEMS: + break; case llvm::Triple::Cygwin: AddPath("/usr/include/w32api", System, true, false, false); break; - case llvm::Triple::MinGW32: - // FIXME: We should be aware of i686-w64-mingw32. - if (triple.getArch() == llvm::Triple::x86_64) - AddPath("c:/mingw/x86_64-w64-mingw32/include", - System, true, false, false); - AddPath("/mingw/include", System, true, false, false); - AddPath("c:/mingw/include", System, true, false, false); + case llvm::Triple::MinGW32: { + // mingw-w64 crt include paths + llvm::sys::Path P(HSOpts.ResourceDir); + P.appendComponent("../../../i686-w64-mingw32/include"); // <sysroot>/i686-w64-mingw32/include + AddPath(P.str(), System, true, false, false); + P = llvm::sys::Path(HSOpts.ResourceDir); + P.appendComponent("../../../x86_64-w64-mingw32/include"); // <sysroot>/x86_64-w64-mingw32/include + AddPath(P.str(), System, true, false, false); + // mingw.org crt include paths + P = llvm::sys::Path(HSOpts.ResourceDir); + P.appendComponent("../../../include"); // <sysroot>/include + AddPath(P.str(), System, true, false, false); + AddPath("/mingw/include", System, true, false, false); + AddPath("c:/mingw/include", System, true, false, false); + } break; + case llvm::Triple::Linux: // Generic Debian multiarch support: if (triple.getArch() == llvm::Triple::x86_64) { @@ -576,11 +590,12 @@ void InitHeaderSearch::AddDefaultCIncludePaths(const llvm::Triple &triple, break; } - AddPath("/usr/include", System, false, false, false); + if ( os != llvm::Triple::RTEMS ) + AddPath("/usr/include", System, false, false, false); } void InitHeaderSearch:: -AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple) { +AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple, const HeaderSearchOptions &HSOpts) { llvm::Triple::OSType os = triple.getOS(); llvm::StringRef CxxIncludeRoot(CXX_INCLUDE_ROOT); if (CxxIncludeRoot != "") { @@ -640,20 +655,19 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple) { AddMinGWCPlusPlusIncludePaths("/usr/lib/gcc", "i686-pc-cygwin", "3.4.4"); break; case llvm::Triple::MinGW32: - // FIXME: We should be aware of i686-w64-mingw32. - if (triple.getArch() == llvm::Triple::x86_64) { - // mingw-w64-20110207 - AddMinGW64CXXPaths("c:/mingw/x86_64-w64-mingw32/include/c++/4.5.3"); - // mingw-w64-20101129 - AddMinGW64CXXPaths("c:/mingw/x86_64-w64-mingw32/include/c++/4.5.2"); - } - // Try gcc 4.5.2 (MSYS) - AddMinGWCPlusPlusIncludePaths("/mingw/lib/gcc", "mingw32", "4.5.2"); - // Try gcc 4.5.0 + // mingw-w64 C++ include paths (i686-w64-mingw32 and x86_64-w64-mingw32) + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.0"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.1"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.2"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.5.3"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.0"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.1"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.6.2"); + AddMinGW64CXXPaths(HSOpts.ResourceDir, "4.7.0"); + // mingw.org C++ include paths + AddMinGWCPlusPlusIncludePaths("/mingw/lib/gcc", "mingw32", "4.5.2"); //MSYS AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.5.0"); - // Try gcc 4.4.0 AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.4.0"); - // Try gcc 4.3.0 AddMinGWCPlusPlusIncludePaths("c:/MinGW/lib/gcc", "mingw32", "4.3.0"); break; case llvm::Triple::DragonFly: @@ -733,6 +747,9 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple) { "x86_64-redhat-linux", "32", "", triple); AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.5.1", "i686-redhat-linux", "", "", triple); + // RHEL5(gcc44) + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.4", + "x86_64-redhat-linux6E", "32", "", triple); // Fedora 13 AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.4.4", "x86_64-redhat-linux", "32", "", triple); @@ -814,6 +831,10 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple) { "x86_64-unknown-linux-gnu", "", "", triple); // Arch Linux gcc 4.6 + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.6.1", + "i686-pc-linux-gnu", "", "", triple); + AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.6.1", + "x86_64-unknown-linux-gnu", "", "", triple); AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.6.0", "i686-pc-linux-gnu", "", "", triple); AddGnuCPlusPlusIncludePaths("/usr/include/c++/4.6.0", @@ -918,8 +939,12 @@ AddDefaultCPlusPlusIncludePaths(const llvm::Triple &triple) { void InitHeaderSearch::AddDefaultSystemIncludePaths(const LangOptions &Lang, const llvm::Triple &triple, const HeaderSearchOptions &HSOpts) { - if (Lang.CPlusPlus && HSOpts.UseStandardCXXIncludes) - AddDefaultCPlusPlusIncludePaths(triple); + if (Lang.CPlusPlus && HSOpts.UseStandardCXXIncludes) { + if (HSOpts.UseLibcxx) + AddPath("/usr/include/c++/v1", CXXSystem, true, false, false); + else + AddDefaultCPlusPlusIncludePaths(triple, HSOpts); + } AddDefaultCIncludePaths(triple, HSOpts); @@ -1017,23 +1042,24 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) { std::vector<DirectoryLookup> SearchList; SearchList.reserve(IncludePath.size()); - /* Quoted arguments go first. */ + // Quoted arguments go first. for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); it != ie; ++it) { if (it->first == Quoted) SearchList.push_back(it->second); } - /* Deduplicate and remember index */ + // Deduplicate and remember index. RemoveDuplicates(SearchList, 0, Verbose); - unsigned quoted = SearchList.size(); + unsigned NumQuoted = SearchList.size(); for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); it != ie; ++it) { if (it->first == Angled) SearchList.push_back(it->second); } - RemoveDuplicates(SearchList, quoted, Verbose); - unsigned angled = SearchList.size(); + + RemoveDuplicates(SearchList, NumQuoted, Verbose); + unsigned NumAngled = SearchList.size(); for (path_iterator it = IncludePath.begin(), ie = IncludePath.end(); it != ie; ++it) { @@ -1047,16 +1073,19 @@ void InitHeaderSearch::Realize(const LangOptions &Lang) { SearchList.push_back(it->second); } - RemoveDuplicates(SearchList, angled, Verbose); + // Remove duplicates across both the Angled and System directories. GCC does + // this and failing to remove duplicates across these two groups breaks + // #include_next. + RemoveDuplicates(SearchList, NumQuoted, Verbose); bool DontSearchCurDir = false; // TODO: set to true if -I- is set? - Headers.SetSearchPaths(SearchList, quoted, angled, DontSearchCurDir); + Headers.SetSearchPaths(SearchList, NumQuoted, NumAngled, DontSearchCurDir); // If verbose, print the list of directories that will be searched. if (Verbose) { llvm::errs() << "#include \"...\" search starts here:\n"; for (unsigned i = 0, e = SearchList.size(); i != e; ++i) { - if (i == quoted) + if (i == NumQuoted) llvm::errs() << "#include <...> search starts here:\n"; const char *Name = SearchList[i].getName(); const char *Suffix; @@ -1084,7 +1113,7 @@ void clang::ApplyHeaderSearchOptions(HeaderSearch &HS, for (unsigned i = 0, e = HSOpts.UserEntries.size(); i != e; ++i) { const HeaderSearchOptions::Entry &E = HSOpts.UserEntries[i]; Init.AddPath(E.Path, E.Group, false, E.IsUserSupplied, E.IsFramework, - !E.IsSysRootRelative); + E.IgnoreSysRoot); } // Add entries from CPATH and friends. diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp index 147a8df03760..9428cd5de0f3 100644 --- a/lib/Frontend/InitPreprocessor.cpp +++ b/lib/Frontend/InitPreprocessor.cpp @@ -221,6 +221,125 @@ static void DefineExactWidthIntType(TargetInfo::IntType Ty, ConstSuffix); } +/// \brief Add definitions required for a smooth interaction between +/// Objective-C++ automatic reference counting and libc++. +static void AddObjCXXARCLibcxxDefines(const LangOptions &LangOpts, + MacroBuilder &Builder) { + Builder.defineMacro("_LIBCPP_PREDEFINED_OBJC_ARC_ADDRESSOF"); + + std::string Result; + { + // Provide overloads of the function std::__1::addressof() that accept + // references to lifetime-qualified objects. libc++'s (more general) + // std::__1::addressof() template fails to instantiate with such types, + // because it attempts to convert the object to a char& before + // dereferencing. + llvm::raw_string_ostream Out(Result); + + Out << "#pragma clang diagnostic push\n" + << "#pragma clang diagnostic ignored \"-Wc++0x-extensions\"\n" + << "namespace std { inline namespace __1 {\n" + << "\n"; + + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\"), " + << "__always_inline__))\n" + << "__attribute__((objc_ownership(strong))) _Tp*\n" + << "addressof(__attribute__((objc_ownership(strong))) _Tp& __x) {\n" + << " return &__x;\n" + << "}\n" + << "\n"; + + if (LangOpts.ObjCRuntimeHasWeak) { + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\")," + << "__always_inline__))\n" + << "__attribute__((objc_ownership(weak))) _Tp*\n" + << "addressof(__attribute__((objc_ownership(weak))) _Tp& __x) {\n" + << " return &__x;\n" + << "};\n" + << "\n"; + } + + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\")," + << "__always_inline__))\n" + << "__attribute__((objc_ownership(autoreleasing))) _Tp*\n" + << "addressof(__attribute__((objc_ownership(autoreleasing))) _Tp& __x) " + << "{\n" + << " return &__x;\n" + << "}\n" + << "\n"; + + Out << "template <class _Tp>\n" + << "inline __attribute__ ((__visibility__(\"hidden\"), " + << "__always_inline__))\n" + << "__unsafe_unretained _Tp* addressof(__unsafe_unretained _Tp& __x)" + << " {\n" + << " return &__x;\n" + << "}\n"; + + Out << "\n" + << "} }\n" + << "#pragma clang diagnostic pop\n" + << "\n"; + } + Builder.append(Result); +} + +/// \brief Add definitions required for a smooth interaction between +/// Objective-C++ automated reference counting and libstdc++ (4.2). +static void AddObjCXXARCLibstdcxxDefines(const LangOptions &LangOpts, + MacroBuilder &Builder) { + Builder.defineMacro("_GLIBCXX_PREDEFINED_OBJC_ARC_IS_SCALAR"); + + std::string Result; + { + // Provide specializations for the __is_scalar type trait so that + // lifetime-qualified objects are not considered "scalar" types, which + // libstdc++ uses as an indicator of the presence of trivial copy, assign, + // default-construct, and destruct semantics (none of which hold for + // lifetime-qualified objects in ARC). + llvm::raw_string_ostream Out(Result); + + Out << "namespace std {\n" + << "\n" + << "struct __true_type;\n" + << "struct __false_type;\n" + << "\n"; + + Out << "template<typename _Tp> struct __is_scalar;\n" + << "\n"; + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(strong))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + if (LangOpts.ObjCRuntimeHasWeak) { + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(weak))) _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + } + + Out << "template<typename _Tp>\n" + << "struct __is_scalar<__attribute__((objc_ownership(autoreleasing)))" + << " _Tp> {\n" + << " enum { __value = 0 };\n" + << " typedef __false_type __type;\n" + << "};\n" + << "\n"; + + Out << "}\n"; + } + Builder.append(Result); +} + static void InitializeStandardPredefinedMacros(const TargetInfo &TI, const LangOptions &LangOpts, const FrontendOptions &FEOpts, @@ -240,11 +359,18 @@ static void InitializeStandardPredefinedMacros(const TargetInfo &TI, } else { if (LangOpts.GNUMode) Builder.defineMacro("__cplusplus"); - else - // C++ [cpp.predefined]p1: + else { + // C++0x [cpp.predefined]p1: + // The name_ _cplusplus is defined to the value 201103L when compiling a + // C++ translation unit. + if (LangOpts.CPlusPlus0x) + Builder.defineMacro("__cplusplus", "201103L"); + // C++03 [cpp.predefined]p1: // The name_ _cplusplus is defined to the value 199711L when compiling a // C++ translation unit. - Builder.defineMacro("__cplusplus", "199711L"); + else + Builder.defineMacro("__cplusplus", "199711L"); + } } if (LangOpts.ObjC1) @@ -312,7 +438,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI, // darwin_constant_cfstrings controls this. This is also dependent // on other things like the runtime I believe. This is set even for C code. - Builder.defineMacro("__CONSTANT_CFSTRINGS__"); + if (!LangOpts.NoConstantCFStrings) + Builder.defineMacro("__CONSTANT_CFSTRINGS__"); if (LangOpts.ObjC2) Builder.defineMacro("OBJC_NEW_PROPERTIES"); @@ -487,6 +614,15 @@ static void InitializePredefinedMacros(const TargetInfo &TI, if (LangOpts.FastRelaxedMath) Builder.defineMacro("__FAST_RELAXED_MATH__"); + if (LangOpts.ObjCAutoRefCount) { + Builder.defineMacro("__weak", "__attribute__((objc_ownership(weak)))"); + Builder.defineMacro("__strong", "__attribute__((objc_ownership(strong)))"); + Builder.defineMacro("__autoreleasing", + "__attribute__((objc_ownership(autoreleasing)))"); + Builder.defineMacro("__unsafe_unretained", + "__attribute__((objc_ownership(none)))"); + } + // Get other target #defines. TI.getTargetDefines(LangOpts, Builder); } @@ -560,6 +696,7 @@ void clang::InitializePreprocessor(Preprocessor &PP, const PreprocessorOptions &InitOpts, const HeaderSearchOptions &HSOpts, const FrontendOptions &FEOpts) { + const LangOptions &LangOpts = PP.getLangOptions(); std::string PredefineBuffer; PredefineBuffer.reserve(4080); llvm::raw_string_ostream Predefines(PredefineBuffer); @@ -575,10 +712,27 @@ void clang::InitializePreprocessor(Preprocessor &PP, Builder.append("# 1 \"<built-in>\" 3"); // Install things like __POWERPC__, __GNUC__, etc into the macro table. - if (InitOpts.UsePredefines) - InitializePredefinedMacros(PP.getTargetInfo(), PP.getLangOptions(), - FEOpts, Builder); - + if (InitOpts.UsePredefines) { + InitializePredefinedMacros(PP.getTargetInfo(), LangOpts, FEOpts, Builder); + + // Install definitions to make Objective-C++ ARC work well with various + // C++ Standard Library implementations. + if (LangOpts.ObjC1 && LangOpts.CPlusPlus && LangOpts.ObjCAutoRefCount) { + switch (InitOpts.ObjCXXARCStandardLibrary) { + case ARCXX_nolib: + break; + + case ARCXX_libcxx: + AddObjCXXARCLibcxxDefines(LangOpts, Builder); + break; + + case ARCXX_libstdcxx: + AddObjCXXARCLibstdcxxDefines(LangOpts, Builder); + break; + } + } + } + // Even with predefines off, some macros are still predefined. // These should all be defined in the preprocessor according to the // current language configuration. diff --git a/lib/Frontend/PrintPreprocessedOutput.cpp b/lib/Frontend/PrintPreprocessedOutput.cpp index b46e04749ba3..c892960a18b9 100644 --- a/lib/Frontend/PrintPreprocessedOutput.cpp +++ b/lib/Frontend/PrintPreprocessedOutput.cpp @@ -26,6 +26,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Config/config.h" #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ErrorHandling.h" #include <cstdio> using namespace clang; @@ -122,6 +123,12 @@ public: virtual void PragmaComment(SourceLocation Loc, const IdentifierInfo *Kind, const std::string &Str); virtual void PragmaMessage(SourceLocation Loc, llvm::StringRef Str); + virtual void PragmaDiagnosticPush(SourceLocation Loc, + llvm::StringRef Namespace); + virtual void PragmaDiagnosticPop(SourceLocation Loc, + llvm::StringRef Namespace); + virtual void PragmaDiagnostic(SourceLocation Loc, llvm::StringRef Namespace, + diag::Mapping Map, llvm::StringRef Str); bool HandleFirstTokOnLine(Token &Tok); bool MoveToLine(SourceLocation Loc) { @@ -189,7 +196,7 @@ bool PrintPPOutputPPCallbacks::MoveToLine(unsigned LineNo) { if (LineNo-CurLine == 1) OS << '\n'; else if (LineNo == CurLine) - return false; // Spelling line moved, but instantiation line didn't. + return false; // Spelling line moved, but expansion line didn't. else { const char *NewLines = "\n\n\n\n\n\n\n\n"; OS.write(NewLines, LineNo-CurLine); @@ -361,12 +368,49 @@ void PrintPPOutputPPCallbacks::PragmaMessage(SourceLocation Loc, EmittedTokensOnThisLine = true; } +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPush(SourceLocation Loc, llvm::StringRef Namespace) { + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic push"; + EmittedTokensOnThisLine = true; +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnosticPop(SourceLocation Loc, llvm::StringRef Namespace) { + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic pop"; + EmittedTokensOnThisLine = true; +} + +void PrintPPOutputPPCallbacks:: +PragmaDiagnostic(SourceLocation Loc, llvm::StringRef Namespace, + diag::Mapping Map, llvm::StringRef Str) { + MoveToLine(Loc); + OS << "#pragma " << Namespace << " diagnostic "; + switch (Map) { + default: llvm_unreachable("unexpected diagnostic kind"); + case diag::MAP_WARNING: + OS << "warning"; + break; + case diag::MAP_ERROR: + OS << "error"; + break; + case diag::MAP_IGNORE: + OS << "ignored"; + break; + case diag::MAP_FATAL: + OS << "fatal"; + break; + } + OS << " \"" << Str << '"'; + EmittedTokensOnThisLine = true; +} /// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this /// is called for the first token on each new line. If this really is the start /// of a new logical line, handle it and return true, otherwise return false. /// This may not be the start of a logical line because the "start of line" -/// marker is set for spelling lines, not instantiation ones. +/// marker is set for spelling lines, not expansion ones. bool PrintPPOutputPPCallbacks::HandleFirstTokOnLine(Token &Tok) { // Figure out what line we went to and insert the appropriate number of // newline characters. diff --git a/lib/Frontend/TextDiagnosticPrinter.cpp b/lib/Frontend/TextDiagnosticPrinter.cpp index 1c47bf7bee93..e49e19a17c8d 100644 --- a/lib/Frontend/TextDiagnosticPrinter.cpp +++ b/lib/Frontend/TextDiagnosticPrinter.cpp @@ -292,8 +292,57 @@ static void SelectInterestingSourceRegion(std::string &SourceLine, } } -void TextDiagnosticPrinter::EmitCaretDiagnostic(Diagnostic::Level Level, - SourceLocation Loc, +/// Look through spelling locations for a macro argument expansion, and +/// if found skip to it so that we can trace the argument rather than the macros +/// in which that argument is used. If no macro argument expansion is found, +/// don't skip anything and return the starting location. +static SourceLocation skipToMacroArgExpansion(const SourceManager &SM, + SourceLocation StartLoc) { + for (SourceLocation L = StartLoc; L.isMacroID(); + L = SM.getImmediateSpellingLoc(L)) { + if (SM.isMacroArgInstantiation(L)) + return L; + } + + // Otherwise just return initial location, there's nothing to skip. + return StartLoc; +} + +/// Gets the location of the immediate macro caller, one level up the stack +/// toward the initial macro typed into the source. +static SourceLocation getImmediateMacroCallerLoc(const SourceManager &SM, + SourceLocation Loc) { + if (!Loc.isMacroID()) return Loc; + + // When we have the location of (part of) an expanded parameter, its spelling + // location points to the argument as typed into the macro call, and + // therefore is used to locate the macro caller. + if (SM.isMacroArgInstantiation(Loc)) + return SM.getImmediateSpellingLoc(Loc); + + // Otherwise, the caller of the macro is located where this macro is + // expanded (while the spelling is part of the macro definition). + return SM.getImmediateInstantiationRange(Loc).first; +} + +/// Gets the location of the immediate macro callee, one level down the stack +/// toward the leaf macro. +static SourceLocation getImmediateMacroCalleeLoc(const SourceManager &SM, + SourceLocation Loc) { + if (!Loc.isMacroID()) return Loc; + + // When we have the location of (part of) an expanded parameter, its + // expansion location points to the unexpanded paramater reference within + // the macro definition (or callee). + if (SM.isMacroArgInstantiation(Loc)) + return SM.getImmediateInstantiationRange(Loc).first; + + // Otherwise, the callee of the macro is located where this location was + // spelled inside the macro definition. + return SM.getImmediateSpellingLoc(Loc); +} + +void TextDiagnosticPrinter::EmitCaretDiagnostic(SourceLocation Loc, CharSourceRange *Ranges, unsigned NumRanges, const SourceManager &SM, @@ -307,38 +356,46 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(Diagnostic::Level Level, assert(!Loc.isInvalid() && "must have a valid source location here"); // If this is a macro ID, first emit information about where this was - // instantiated (recursively) then emit information about where the token was + // expanded (recursively) then emit information about where the token was // spelled from. if (!Loc.isFileID()) { - // Whether to suppress printing this macro instantiation. + // Whether to suppress printing this macro expansion. bool Suppressed = OnMacroInst >= MacroSkipStart && OnMacroInst < MacroSkipEnd; - - SourceLocation OneLevelUp = SM.getImmediateInstantiationRange(Loc).first; + // When processing macros, skip over the expansions leading up to + // a macro argument, and trace the argument's expansion stack instead. + Loc = skipToMacroArgExpansion(SM, Loc); + + SourceLocation OneLevelUp = getImmediateMacroCallerLoc(SM, Loc); + // FIXME: Map ranges? - EmitCaretDiagnostic(Level, OneLevelUp, Ranges, NumRanges, SM, 0, 0, Columns, + EmitCaretDiagnostic(OneLevelUp, Ranges, NumRanges, SM, + Hints, NumHints, Columns, OnMacroInst + 1, MacroSkipStart, MacroSkipEnd); - + // Map the location. - Loc = SM.getImmediateSpellingLoc(Loc); + Loc = getImmediateMacroCalleeLoc(SM, Loc); // Map the ranges. for (unsigned i = 0; i != NumRanges; ++i) { CharSourceRange &R = Ranges[i]; SourceLocation S = R.getBegin(), E = R.getEnd(); if (S.isMacroID()) - R.setBegin(SM.getImmediateSpellingLoc(S)); + R.setBegin(getImmediateMacroCalleeLoc(SM, S)); if (E.isMacroID()) - R.setEnd(SM.getImmediateSpellingLoc(E)); + R.setEnd(getImmediateMacroCalleeLoc(SM, E)); } if (!Suppressed) { + // Don't print recursive expansion notes from an expansion note. + Loc = SM.getSpellingLoc(Loc); + // Get the pretty name, according to #line directives etc. PresumedLoc PLoc = SM.getPresumedLoc(Loc); if (PLoc.isInvalid()) return; - + // If this diagnostic is not in the main file, print out the // "included from" lines. if (LastWarningLoc != PLoc.getIncludeLoc()) { @@ -353,9 +410,9 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(Diagnostic::Level Level, OS << PLoc.getColumn() << ':'; OS << ' '; } - OS << "note: instantiated from:\n"; - - EmitCaretDiagnostic(Level, Loc, Ranges, NumRanges, SM, Hints, NumHints, + OS << "note: expanded from:\n"; + + EmitCaretDiagnostic(Loc, Ranges, NumRanges, SM, 0, 0, Columns, OnMacroInst + 1, MacroSkipStart, MacroSkipEnd); return; @@ -364,13 +421,13 @@ void TextDiagnosticPrinter::EmitCaretDiagnostic(Diagnostic::Level Level, if (OnMacroInst == MacroSkipStart) { // Tell the user that we've skipped contexts. OS << "note: (skipping " << (MacroSkipEnd - MacroSkipStart) - << " contexts in backtrace; use -fmacro-backtrace-limit=0 to see " + << " expansions in backtrace; use -fmacro-backtrace-limit=0 to see " "all)\n"; } return; } - + // Decompose the location into a FID/Offset pair. std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(Loc); FileID FID = LocInfo.first; @@ -769,6 +826,20 @@ static bool PrintWordWrapped(llvm::raw_ostream &OS, return true; } +/// Get the presumed location of a diagnostic message. This computes the +/// presumed location for the top of any macro backtrace when present. +static PresumedLoc getDiagnosticPresumedLoc(const SourceManager &SM, + SourceLocation Loc) { + // This is a condensed form of the algorithm used by EmitCaretDiagnostic to + // walk to the top of the macro call stack. + while (Loc.isMacroID()) { + Loc = skipToMacroArgExpansion(SM, Loc); + Loc = getImmediateMacroCallerLoc(SM, Loc); + } + + return SM.getPresumedLoc(Loc); +} + void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, const DiagnosticInfo &Info) { // Default implementation (Warnings/errors count). @@ -787,7 +858,7 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, // if enabled. if (Info.getLocation().isValid()) { const SourceManager &SM = Info.getSourceManager(); - PresumedLoc PLoc = SM.getPresumedLoc(Info.getLocation()); + PresumedLoc PLoc = getDiagnosticPresumedLoc(SM, Info.getLocation()); if (PLoc.isInvalid()) { // At least print the file name if available: FileID FID = SM.getFileID(Info.getLocation()); @@ -1040,15 +1111,17 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, } } + const SourceManager &SM = LastLoc.getManager(); unsigned MacroInstSkipStart = 0, MacroInstSkipEnd = 0; if (DiagOpts && DiagOpts->MacroBacktraceLimit && !LastLoc.isFileID()) { - // Compute the length of the macro-instantiation backtrace, so that we + // Compute the length of the macro-expansion backtrace, so that we // can establish which steps in the macro backtrace we'll skip. SourceLocation Loc = LastLoc; unsigned Depth = 0; do { ++Depth; - Loc = LastLoc.getManager().getImmediateInstantiationRange(Loc).first; + Loc = skipToMacroArgExpansion(SM, Loc); + Loc = getImmediateMacroCallerLoc(SM, Loc); } while (!Loc.isFileID()); if (Depth > DiagOpts->MacroBacktraceLimit) { @@ -1058,7 +1131,7 @@ void TextDiagnosticPrinter::HandleDiagnostic(Diagnostic::Level Level, } } - EmitCaretDiagnostic(Level, LastLoc, Ranges, NumRanges, LastLoc.getManager(), + EmitCaretDiagnostic(LastLoc, Ranges, NumRanges, LastLoc.getManager(), Info.getFixItHints(), Info.getNumFixItHints(), DiagOpts->MessageLength, diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 720ce2adf123..b8e4329c07f9 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_USED_LIBS clangDriver clangFrontend clangRewrite clangCodeGen - clangStaticAnalyzerFrontend clangStaticAnalyzerCheckers clangStaticAnalyzerCore) + clangStaticAnalyzerFrontend clangStaticAnalyzerCheckers clangStaticAnalyzerCore + clangARCMigrate) add_clang_library(clangFrontendTool ExecuteCompilerInvocation.cpp diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index 664b53351dc1..f2db3ae74191 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -14,6 +14,7 @@ #include "clang/FrontendTool/Utils.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "clang/ARCMigrate/ARCMTActions.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/Driver/CC1Options.h" #include "clang/Driver/OptTable.h" @@ -38,7 +39,6 @@ static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { case ASTDumpXML: return new ASTDumpXMLAction(); case ASTPrint: return new ASTPrintAction(); case ASTView: return new ASTViewAction(); - case BoostCon: return new BoostConAction(); case CreateModule: return 0; case DumpRawTokens: return new DumpRawTokensAction(); case DumpTokens: return new DumpTokensAction(); @@ -89,6 +89,21 @@ static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { if (!Act) return 0; + // Potentially wrap the base FE action in an ARC Migrate Tool action. + switch (CI.getFrontendOpts().ARCMTAction) { + case FrontendOptions::ARCMT_None: + break; + case FrontendOptions::ARCMT_Check: + Act = new arcmt::CheckAction(Act); + break; + case FrontendOptions::ARCMT_Modify: + Act = new arcmt::ModifyAction(Act); + break; + case FrontendOptions::ARCMT_Migrate: + Act = new arcmt::MigrateAction(Act, CI.getFrontendOpts().ARCMTMigrateDir); + break; + } + // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!CI.getFrontendOpts().ASTMergeFiles.empty()) diff --git a/lib/Headers/float.h b/lib/Headers/float.h index 28fb882bd24a..6eede0b6ba47 100644 --- a/lib/Headers/float.h +++ b/lib/Headers/float.h @@ -24,6 +24,48 @@ #ifndef __FLOAT_H #define __FLOAT_H +/* If we're on MinGW, fall baack to the system's float.h, which might have + * additional definitions provided for Windows. + * For more details see http://msdn.microsoft.com/en-us/library/y0ybw9fy.aspx + */ +#if defined(__MINGW32__) && \ + defined(__has_include_next) && __has_include_next(<float.h>) +# include_next <float.h> + +/* Undefine anything that we'll be redefining below. */ +# undef FLT_EVAL_METHOD +# undef FLT_ROUNDS +# undef FLT_RADIX +# undef FLT_MANT_DIG +# undef DBL_MANT_DIG +# undef LDBL_MANT_DIG +# undef DECIMAL_DIG +# undef FLT_DIG +# undef DBL_DIG +# undef LDBL_DIG +# undef FLT_MIN_EXP +# undef DBL_MIN_EXP +# undef LDBL_MIN_EXP +# undef FLT_MIN_10_EXP +# undef DBL_MIN_10_EXP +# undef LDBL_MIN_10_EXP +# undef FLT_MAX_EXP +# undef DBL_MAX_EXP +# undef LDBL_MAX_EXP +# undef FLT_MAX_10_EXP +# undef DBL_MAX_10_EXP +# undef LDBL_MAX_10_EXP +# undef FLT_MAX +# undef DBL_MAX +# undef LDBL_MAX +# undef FLT_EPSILON +# undef DBL_EPSILON +# undef LDBL_EPSILON +# undef FLT_MIN +# undef DBL_MIN +# undef LDBL_MIN +#endif + /* Characteristics of floating point types, C99 5.2.4.2.2 */ #define FLT_EVAL_METHOD __FLT_EVAL_METHOD__ diff --git a/lib/Headers/stdarg.h b/lib/Headers/stdarg.h index c36ab12019fe..2957bf058ea8 100644 --- a/lib/Headers/stdarg.h +++ b/lib/Headers/stdarg.h @@ -39,7 +39,7 @@ typedef __builtin_va_list va_list; */ #define __va_copy(d,s) __builtin_va_copy(d,s) -#if __STDC_VERSION__ >= 199900L || !defined(__STRICT_ANSI__) +#if __STDC_VERSION__ >= 199900L || __cplusplus >= 201103L || !defined(__STRICT_ANSI__) #define va_copy(dest, src) __builtin_va_copy(dest, src) #endif diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp index bb4388195a32..86ab9564a235 100644 --- a/lib/Lex/HeaderSearch.cpp +++ b/lib/Lex/HeaderSearch.cpp @@ -123,10 +123,8 @@ const FileEntry *DirectoryLookup::LookupFile( llvm::SmallString<1024> TmpDir; if (isNormalDir()) { // Concatenate the requested file onto the directory. - // FIXME: Portability. Filename concatenation should be in sys::Path. - TmpDir += getDir()->getName(); - TmpDir.push_back('/'); - TmpDir.append(Filename.begin(), Filename.end()); + TmpDir = getDir()->getName(); + llvm::sys::path::append(TmpDir, Filename); if (SearchPath != NULL) { llvm::StringRef SearchPathRef(getDir()->getName()); SearchPath->clear(); diff --git a/lib/Lex/Lexer.cpp b/lib/Lex/Lexer.cpp index 3b1149c08af3..a28b8f6e7b9f 100644 --- a/lib/Lex/Lexer.cpp +++ b/lib/Lex/Lexer.cpp @@ -156,7 +156,7 @@ Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *FromFile, /// /// On entrance to this routine, TokStartLoc is a macro location which has a /// spelling loc that indicates the bytes to be lexed for the token and an -/// instantiation location that indicates where all lexed tokens should be +/// expansion location that indicates where all lexed tokens should be /// "expanded from". /// /// FIXME: It would really be nice to make _Pragma just be a wrapper around a @@ -166,8 +166,8 @@ Lexer::Lexer(FileID FID, const llvm::MemoryBuffer *FromFile, /// out of the critical path of the lexer! /// Lexer *Lexer::Create_PragmaLexer(SourceLocation SpellingLoc, - SourceLocation InstantiationLocStart, - SourceLocation InstantiationLocEnd, + SourceLocation ExpansionLocStart, + SourceLocation ExpansionLocEnd, unsigned TokLen, Preprocessor &PP) { SourceManager &SM = PP.getSourceManager(); @@ -188,8 +188,8 @@ Lexer *Lexer::Create_PragmaLexer(SourceLocation SpellingLoc, // Set the SourceLocation with the remapping information. This ensures that // GetMappedTokenLoc will remap the tokens as they are lexed. L->FileLoc = SM.createInstantiationLoc(SM.getLocForStartOfFile(SpellingFID), - InstantiationLocStart, - InstantiationLocEnd, TokLen); + ExpansionLocStart, + ExpansionLocEnd, TokLen); // Ensure that the lexer thinks it is inside a directive, so that end \n will // return an EOD token. @@ -621,7 +621,7 @@ SourceLocation Lexer::AdvanceToTokenCharacter(SourceLocation TokStart, unsigned CharNo, const SourceManager &SM, const LangOptions &Features) { - // Figure out how many physical characters away the specified instantiation + // Figure out how many physical characters away the specified expansion // character is. This needs to take into consideration newlines and // trigraphs. bool Invalid = false; @@ -679,9 +679,17 @@ SourceLocation Lexer::AdvanceToTokenCharacter(SourceLocation TokStart, SourceLocation Lexer::getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &Features) { - if (Loc.isInvalid() || !Loc.isFileID()) + if (Loc.isInvalid()) return SourceLocation(); - + + if (Loc.isMacroID()) { + if (Offset > 0 || !isAtEndOfMacroExpansion(Loc, SM, Features)) + return SourceLocation(); // Points inside the macro expansion. + + // Continue and find the location just after the macro expansion. + Loc = SM.getInstantiationRange(Loc).second; + } + unsigned Len = Lexer::MeasureTokenLength(Loc, SM, Features); if (Len > Offset) Len = Len - Offset; @@ -691,6 +699,58 @@ SourceLocation Lexer::getLocForEndOfToken(SourceLocation Loc, unsigned Offset, return Loc.getFileLocWithOffset(Len); } +/// \brief Returns true if the given MacroID location points at the first +/// token of the macro expansion. +bool Lexer::isAtStartOfMacroExpansion(SourceLocation loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc"); + + std::pair<FileID, unsigned> infoLoc = SM.getDecomposedLoc(loc); + // FIXME: If the token comes from the macro token paste operator ('##') + // this function will always return false; + if (infoLoc.second > 0) + return false; // Does not point at the start of token. + + SourceLocation expansionLoc = + SM.getSLocEntry(infoLoc.first) + .getInstantiation().getInstantiationLocStart(); + if (expansionLoc.isFileID()) + return true; // No other macro expansions, this is the first. + + return isAtStartOfMacroExpansion(expansionLoc, SM, LangOpts); +} + +/// \brief Returns true if the given MacroID location points at the last +/// token of the macro expansion. +bool Lexer::isAtEndOfMacroExpansion(SourceLocation loc, + const SourceManager &SM, + const LangOptions &LangOpts) { + assert(loc.isValid() && loc.isMacroID() && "Expected a valid macro loc"); + + SourceLocation spellLoc = SM.getSpellingLoc(loc); + unsigned tokLen = MeasureTokenLength(spellLoc, SM, LangOpts); + if (tokLen == 0) + return false; + + FileID FID = SM.getFileID(loc); + SourceLocation afterLoc = loc.getFileLocWithOffset(tokLen+1); + if (!SM.isBeforeInSourceLocationOffset(afterLoc, SM.getNextOffset())) + return true; // We got past the last FileID, this points to the last token. + + // FIXME: If the token comes from the macro token paste operator ('##') + // or the stringify operator ('#') this function will always return false; + if (FID == SM.getFileID(afterLoc)) + return false; // Still in the same FileID, does not point to the last token. + + SourceLocation expansionLoc = + SM.getSLocEntry(FID).getInstantiation().getInstantiationLocEnd(); + if (expansionLoc.isFileID()) + return true; // No other macro expansions. + + return isAtEndOfMacroExpansion(expansionLoc, SM, LangOpts); +} + //===----------------------------------------------------------------------===// // Character information. //===----------------------------------------------------------------------===// @@ -829,7 +889,7 @@ static inline bool isNumberBody(unsigned char c) { //===----------------------------------------------------------------------===// /// GetMappedTokenLoc - If lexing out of a 'mapped buffer', where we pretend the -/// lexer buffer was all instantiated at a single point, perform the mapping. +/// lexer buffer was all expanded at a single point, perform the mapping. /// This is currently only used for _Pragma implementation, so it is the slow /// path of the hot getSourceLocation method. Do not allow it to be inlined. static LLVM_ATTRIBUTE_NOINLINE SourceLocation GetMappedTokenLoc( @@ -837,14 +897,14 @@ static LLVM_ATTRIBUTE_NOINLINE SourceLocation GetMappedTokenLoc( static SourceLocation GetMappedTokenLoc(Preprocessor &PP, SourceLocation FileLoc, unsigned CharNo, unsigned TokLen) { - assert(FileLoc.isMacroID() && "Must be an instantiation"); + assert(FileLoc.isMacroID() && "Must be a macro expansion"); // Otherwise, we're lexing "mapped tokens". This is used for things like - // _Pragma handling. Combine the instantiation location of FileLoc with the + // _Pragma handling. Combine the expansion location of FileLoc with the // spelling location. SourceManager &SM = PP.getSourceManager(); - // Create a new SLoc which is expanded from Instantiation(FileLoc) but whose + // Create a new SLoc which is expanded from Expansion(FileLoc) but whose // characters come from spelling(FileLoc)+Offset. SourceLocation SpellingLoc = SM.getSpellingLoc(FileLoc); SpellingLoc = SpellingLoc.getFileLocWithOffset(CharNo); diff --git a/lib/Lex/MacroArgs.cpp b/lib/Lex/MacroArgs.cpp index dee7da38aaa0..968c15e3c27b 100644 --- a/lib/Lex/MacroArgs.cpp +++ b/lib/Lex/MacroArgs.cpp @@ -185,7 +185,8 @@ MacroArgs::getPreExpArgument(unsigned Arg, const MacroInfo *MI, /// a character literal for the Microsoft charize (#@) extension. /// Token MacroArgs::StringifyArgument(const Token *ArgToks, - Preprocessor &PP, bool Charify) { + Preprocessor &PP, bool Charify, + SourceLocation hashInstLoc) { Token Tok; Tok.startToken(); Tok.setKind(Charify ? tok::char_constant : tok::string_literal); @@ -273,14 +274,15 @@ Token MacroArgs::StringifyArgument(const Token *ArgToks, } } - PP.CreateString(&Result[0], Result.size(), Tok); + PP.CreateString(&Result[0], Result.size(), Tok, hashInstLoc); return Tok; } /// getStringifiedArgument - Compute, cache, and return the specified argument /// that has been 'stringified' as required by the # operator. const Token &MacroArgs::getStringifiedArgument(unsigned ArgNo, - Preprocessor &PP) { + Preprocessor &PP, + SourceLocation hashInstLoc) { assert(ArgNo < NumUnexpArgTokens && "Invalid argument number!"); if (StringifiedArgs.empty()) { StringifiedArgs.resize(getNumArguments()); @@ -288,6 +290,7 @@ const Token &MacroArgs::getStringifiedArgument(unsigned ArgNo, sizeof(StringifiedArgs[0])*getNumArguments()); } if (StringifiedArgs[ArgNo].isNot(tok::string_literal)) - StringifiedArgs[ArgNo] = StringifyArgument(getUnexpArgument(ArgNo), PP); + StringifiedArgs[ArgNo] = StringifyArgument(getUnexpArgument(ArgNo), PP, + /*Charify=*/false, hashInstLoc); return StringifiedArgs[ArgNo]; } diff --git a/lib/Lex/MacroArgs.h b/lib/Lex/MacroArgs.h index 6ff4856b4e1c..a962dacf7c93 100644 --- a/lib/Lex/MacroArgs.h +++ b/lib/Lex/MacroArgs.h @@ -20,6 +20,7 @@ namespace clang { class MacroInfo; class Preprocessor; class Token; + class SourceLocation; /// MacroArgs - An instance of this class captures information about /// the formal arguments specified to a function-like macro invocation. @@ -86,7 +87,8 @@ public: /// getStringifiedArgument - Compute, cache, and return the specified argument /// that has been 'stringified' as required by the # operator. - const Token &getStringifiedArgument(unsigned ArgNo, Preprocessor &PP); + const Token &getStringifiedArgument(unsigned ArgNo, Preprocessor &PP, + SourceLocation hashInstLoc); /// getNumArguments - Return the number of arguments passed into this macro /// invocation. @@ -106,7 +108,8 @@ public: /// a character literal for the Microsoft charize (#@) extension. /// static Token StringifyArgument(const Token *ArgToks, - Preprocessor &PP, bool Charify = false); + Preprocessor &PP, bool Charify, + SourceLocation hashInstLoc); /// deallocate - This should only be called by the Preprocessor when managing diff --git a/lib/Lex/MacroInfo.cpp b/lib/Lex/MacroInfo.cpp index 66d87a19386a..0a16a2567219 100644 --- a/lib/Lex/MacroInfo.cpp +++ b/lib/Lex/MacroInfo.cpp @@ -25,6 +25,7 @@ MacroInfo::MacroInfo(SourceLocation DefLoc) : Location(DefLoc) { IsUsed = false; IsAllowRedefinitionsWithoutWarning = false; IsWarnIfUnused = false; + IsDefinitionLengthCached = false; ArgumentList = 0; NumArguments = 0; @@ -43,11 +44,42 @@ MacroInfo::MacroInfo(const MacroInfo &MI, llvm::BumpPtrAllocator &PPAllocator) { IsUsed = MI.IsUsed; IsAllowRedefinitionsWithoutWarning = MI.IsAllowRedefinitionsWithoutWarning; IsWarnIfUnused = MI.IsWarnIfUnused; + IsDefinitionLengthCached = MI.IsDefinitionLengthCached; + DefinitionLength = MI.DefinitionLength; ArgumentList = 0; NumArguments = 0; setArgumentList(MI.ArgumentList, MI.NumArguments, PPAllocator); } +unsigned MacroInfo::getDefinitionLengthSlow(SourceManager &SM) const { + assert(!IsDefinitionLengthCached); + IsDefinitionLengthCached = true; + + if (ReplacementTokens.empty()) + return (DefinitionLength = 0); + + const Token &firstToken = ReplacementTokens.front(); + const Token &lastToken = ReplacementTokens.back(); + SourceLocation macroStart = firstToken.getLocation(); + SourceLocation macroEnd = lastToken.getLocation(); + assert(macroStart.isValid() && macroEnd.isValid()); + assert((macroStart.isFileID() || firstToken.is(tok::comment)) && + "Macro defined in macro?"); + assert((macroEnd.isFileID() || lastToken.is(tok::comment)) && + "Macro defined in macro?"); + std::pair<FileID, unsigned> + startInfo = SM.getDecomposedInstantiationLoc(macroStart); + std::pair<FileID, unsigned> + endInfo = SM.getDecomposedInstantiationLoc(macroEnd); + assert(startInfo.first == endInfo.first && + "Macro definition spanning multiple FileIDs ?"); + assert(startInfo.second <= endInfo.second); + DefinitionLength = endInfo.second - startInfo.second; + DefinitionLength += lastToken.getLength(); + + return DefinitionLength; +} + /// isIdenticalTo - Return true if the specified macro definition is equal to /// this macro in spelling, arguments, and whitespace. This is used to emit /// duplicate definition warnings. This implements the rules in C99 6.10.3. diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index 66e44bbc6dd0..4af5fabe5c80 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -784,8 +784,7 @@ void Preprocessor::HandleLineDirective(Token &Tok) { Diag(StrTok, diag::err_pp_linemarker_invalid_filename); return DiscardUntilEndOfDirective(); } - FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString(), - Literal.GetStringLength()); + FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString()); // Verify that there is nothing after the string, other than EOD. Because // of C99 6.10.4p5, macros that expand to empty tokens are ok. @@ -918,8 +917,7 @@ void Preprocessor::HandleDigitDirective(Token &DigitTok) { Diag(StrTok, diag::err_pp_linemarker_invalid_filename); return DiscardUntilEndOfDirective(); } - FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString(), - Literal.GetStringLength()); + FilenameID = SourceMgr.getLineTableFilenameID(Literal.GetString()); // If a filename was present, read any flags that are present. if (ReadLineMarkerFlags(IsFileEntry, IsFileExit, @@ -1182,16 +1180,17 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, const FileEntry *File = LookupFile( Filename, isAngled, LookupFrom, CurDir, Callbacks ? &SearchPath : NULL, Callbacks ? &RelativePath : NULL); - if (File == 0) { - Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; - return; - } // Notify the callback object that we've seen an inclusion directive. if (Callbacks) Callbacks->InclusionDirective(HashLoc, IncludeTok, Filename, isAngled, File, End, SearchPath, RelativePath); + if (File == 0) { + Diag(FilenameTok, diag::warn_pp_file_not_found) << Filename; + return; + } + // The #included file will be considered to be a system header if either it is // in a system include directory, or if the #includer is a system include // header. @@ -1210,10 +1209,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, // Look up the file, create a File ID for it. FileID FID = SourceMgr.createFileID(File, FilenameTok.getLocation(), FileCharacter); - if (FID.isInvalid()) { - Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; - return; - } + assert(!FID.isInvalid() && "Expected valid file ID"); // Finally, if all is good, enter the new file! EnterSourceFile(FID, CurDir, FilenameTok.getLocation()); @@ -1599,7 +1595,7 @@ void Preprocessor::HandleUndefDirective(Token &UndefTok) { // If the macro is not defined, this is a noop undef, just return. if (MI == 0) return; - if (!MI->isUsed()) + if (!MI->isUsed() && MI->isWarnIfUnused()) Diag(MI->getDefinitionLoc(), diag::pp_macro_not_used); // If the callbacks want to know, tell them about the macro #undef. diff --git a/lib/Lex/PPLexerChange.cpp b/lib/Lex/PPLexerChange.cpp index bf0a7fbfef18..bf28199b888a 100644 --- a/lib/Lex/PPLexerChange.cpp +++ b/lib/Lex/PPLexerChange.cpp @@ -265,6 +265,10 @@ bool Preprocessor::HandleEndOfTokenLexer(Token &Result) { assert(CurTokenLexer && !CurPPLexer && "Ending a macro when currently in a #include file!"); + if (!MacroExpandingLexersStack.empty() && + MacroExpandingLexersStack.back().first == CurTokenLexer.get()) + removeCachedMacroExpandedTokensOfLastLexer(); + // Delete or cache the now-dead macro expander. if (NumCachedTokenLexers == TokenLexerCacheSize) CurTokenLexer.reset(); diff --git a/lib/Lex/PPMacroExpansion.cpp b/lib/Lex/PPMacroExpansion.cpp index 01cd75fa8485..ecd4d4cfc68b 100644 --- a/lib/Lex/PPMacroExpansion.cpp +++ b/lib/Lex/PPMacroExpansion.cpp @@ -22,6 +22,7 @@ #include "clang/Lex/CodeCompletionHandler.h" #include "clang/Lex/ExternalPreprocessorSource.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Config/config.h" #include "llvm/Support/raw_ostream.h" #include <cstdio> @@ -194,9 +195,9 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, /// invocation. MacroArgs *Args = 0; - // Remember where the end of the instantiation occurred. For an object-like + // Remember where the end of the expansion occurred. For an object-like // macro, this is the identifier. For a function-like macro, this is the ')'. - SourceLocation InstantiationEnd = Identifier.getLocation(); + SourceLocation ExpansionEnd = Identifier.getLocation(); // If this is a function-like macro, read the arguments. if (MI->isFunctionLike()) { @@ -209,7 +210,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // Preprocessor directives used inside macro arguments are not portable, and // this enables the warning. InMacroArgs = true; - Args = ReadFunctionLikeMacroArgs(Identifier, MI, InstantiationEnd); + Args = ReadFunctionLikeMacroArgs(Identifier, MI, ExpansionEnd); // Finished parsing args. InMacroArgs = false; @@ -229,8 +230,8 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, // If we started lexing a macro, enter the macro expansion body. - // Remember where the token is instantiated. - SourceLocation InstantiateLoc = Identifier.getLocation(); + // Remember where the token is expanded. + SourceLocation ExpandLoc = Identifier.getLocation(); // If this macro expands to no tokens, don't bother to push it onto the // expansion stack, only to take it right back off. @@ -254,7 +255,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, if (HadLeadingSpace) Identifier.setFlag(Token::LeadingSpace); } Identifier.setFlag(Token::LeadingEmptyMacro); - LastEmptyMacroInstantiationLoc = InstantiateLoc; + LastEmptyMacroExpansionLoc = ExpandLoc; ++NumFastMacroExpanded; return false; @@ -280,11 +281,11 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, Identifier.setFlagValue(Token::StartOfLine , isAtStartOfLine); Identifier.setFlagValue(Token::LeadingSpace, hasLeadingSpace); - // Update the tokens location to include both its instantiation and physical + // Update the tokens location to include both its expansion and physical // locations. SourceLocation Loc = - SourceMgr.createInstantiationLoc(Identifier.getLocation(), InstantiateLoc, - InstantiationEnd,Identifier.getLength()); + SourceMgr.createInstantiationLoc(Identifier.getLocation(), ExpandLoc, + ExpansionEnd,Identifier.getLength()); Identifier.setLocation(Loc); // If this is a disabled macro or #define X X, we must mark the result as @@ -302,7 +303,7 @@ bool Preprocessor::HandleMacroExpandedIdentifier(Token &Identifier, } // Start expanding the macro. - EnterMacro(Identifier, InstantiationEnd, Args); + EnterMacro(Identifier, ExpansionEnd, Args); // Now that the macro is at the top of the include stack, ask the // preprocessor to read the next token from it. @@ -490,6 +491,46 @@ MacroArgs *Preprocessor::ReadFunctionLikeMacroArgs(Token &MacroName, isVarargsElided, *this); } +/// \brief Keeps macro expanded tokens for TokenLexers. +// +/// Works like a stack; a TokenLexer adds the macro expanded tokens that is +/// going to lex in the cache and when it finishes the tokens are removed +/// from the end of the cache. +Token *Preprocessor::cacheMacroExpandedTokens(TokenLexer *tokLexer, + llvm::ArrayRef<Token> tokens) { + assert(tokLexer); + if (tokens.empty()) + return 0; + + size_t newIndex = MacroExpandedTokens.size(); + bool cacheNeedsToGrow = tokens.size() > + MacroExpandedTokens.capacity()-MacroExpandedTokens.size(); + MacroExpandedTokens.append(tokens.begin(), tokens.end()); + + if (cacheNeedsToGrow) { + // Go through all the TokenLexers whose 'Tokens' pointer points in the + // buffer and update the pointers to the (potential) new buffer array. + for (unsigned i = 0, e = MacroExpandingLexersStack.size(); i != e; ++i) { + TokenLexer *prevLexer; + size_t tokIndex; + llvm::tie(prevLexer, tokIndex) = MacroExpandingLexersStack[i]; + prevLexer->Tokens = MacroExpandedTokens.data() + tokIndex; + } + } + + MacroExpandingLexersStack.push_back(std::make_pair(tokLexer, newIndex)); + return MacroExpandedTokens.data() + newIndex; +} + +void Preprocessor::removeCachedMacroExpandedTokensOfLastLexer() { + assert(!MacroExpandingLexersStack.empty()); + size_t tokIndex = MacroExpandingLexersStack.back().second; + assert(tokIndex < MacroExpandedTokens.size()); + // Pop the cached macro expanded tokens from the end. + MacroExpandedTokens.resize(tokIndex); + MacroExpandingLexersStack.pop_back(); +} + /// ComputeDATE_TIME - Compute the current time, enter it into the specified /// scratch buffer, then return DATELoc/TIMELoc locations with the position of /// the identifier tokens inserted. @@ -551,6 +592,11 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("cxx_exceptions", LangOpts.Exceptions) .Case("cxx_rtti", LangOpts.RTTI) .Case("enumerator_attributes", true) + // Objective-C features + .Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? + .Case("objc_arc", LangOpts.ObjCAutoRefCount) + .Case("objc_arc_weak", LangOpts.ObjCAutoRefCount && + LangOpts.ObjCRuntimeHasWeak) .Case("objc_nonfragile_abi", LangOpts.ObjCNonFragileABI) .Case("objc_weak_class", LangOpts.ObjCNonFragileABI) .Case("ownership_holds", true) @@ -787,10 +833,10 @@ void Preprocessor::ExpandBuiltinMacro(Token &Tok) { Loc = AdvanceToTokenCharacter(Loc, 0); // One wrinkle here is that GCC expands __LINE__ to location of the *end* of - // a macro instantiation. This doesn't matter for object-like macros, but + // a macro expansion. This doesn't matter for object-like macros, but // can matter for a function-like macro that expands to contain __LINE__. - // Skip down through instantiation points until we find a file loc for the - // end of the instantiation history. + // Skip down through expansion points until we find a file loc for the + // end of the expansion history. Loc = SourceMgr.getInstantiationRange(Loc).second; PresumedLoc PLoc = SourceMgr.getPresumedLoc(Loc); diff --git a/lib/Lex/Pragma.cpp b/lib/Lex/Pragma.cpp index 23855d4a4748..e6b28c13317b 100644 --- a/lib/Lex/Pragma.cpp +++ b/lib/Lex/Pragma.cpp @@ -326,9 +326,7 @@ void Preprocessor::HandlePragmaSystemHeader(Token &SysHeaderTok) { if (PLoc.isInvalid()) return; - unsigned FilenameLen = strlen(PLoc.getFilename()); - unsigned FilenameID = SourceMgr.getLineTableFilenameID(PLoc.getFilename(), - FilenameLen); + unsigned FilenameID = SourceMgr.getLineTableFilenameID(PLoc.getFilename()); // Notify the client, if desired, that we are in a new source file. if (Callbacks) @@ -370,7 +368,7 @@ void Preprocessor::HandlePragmaDependency(Token &DependencyTok) { const DirectoryLookup *CurDir; const FileEntry *File = LookupFile(Filename, isAngled, 0, CurDir, NULL, NULL); if (File == 0) { - Diag(FilenameTok, diag::err_pp_file_not_found) << Filename; + Diag(FilenameTok, diag::warn_pp_file_not_found) << Filename; return; } @@ -454,8 +452,7 @@ void Preprocessor::HandlePragmaComment(Token &Tok) { return; } - ArgumentString = std::string(Literal.GetString(), - Literal.GetString()+Literal.GetStringLength()); + ArgumentString = Literal.GetString(); } // FIXME: If the kind is "compiler" warn if the string is present (it is @@ -531,7 +528,7 @@ void Preprocessor::HandlePragmaMessage(Token &Tok) { return; } - llvm::StringRef MessageString(Literal.GetString(), Literal.GetStringLength()); + llvm::StringRef MessageString(Literal.GetString()); if (ExpectClosingParen) { if (Tok.isNot(tok::r_paren)) { @@ -839,8 +836,11 @@ struct PragmaDebugHandler : public PragmaHandler { /// PragmaDiagnosticHandler - e.g. '#pragma GCC diagnostic ignored "-Wformat"' struct PragmaDiagnosticHandler : public PragmaHandler { +private: + const char *Namespace; public: - explicit PragmaDiagnosticHandler() : PragmaHandler("diagnostic") {} + explicit PragmaDiagnosticHandler(const char *NS) : + PragmaHandler("diagnostic"), Namespace(NS) {} virtual void HandlePragma(Preprocessor &PP, PragmaIntroducerKind Introducer, Token &DiagToken) { SourceLocation DiagLoc = DiagToken.getLocation(); @@ -851,6 +851,7 @@ public: return; } IdentifierInfo *II = Tok.getIdentifierInfo(); + PPCallbacks *Callbacks = PP.getPPCallbacks(); diag::Mapping Map; if (II->isStr("warning")) @@ -864,10 +865,13 @@ public: else if (II->isStr("pop")) { if (!PP.getDiagnostics().popMappings(DiagLoc)) PP.Diag(Tok, diag::warn_pragma_diagnostic_cannot_pop); - + else if (Callbacks) + Callbacks->PragmaDiagnosticPop(DiagLoc, Namespace); return; } else if (II->isStr("push")) { PP.getDiagnostics().pushMappings(DiagLoc); + if (Callbacks) + Callbacks->PragmaDiagnosticPush(DiagLoc, Namespace); return; } else { PP.Diag(Tok, diag::warn_pragma_diagnostic_invalid); @@ -906,7 +910,7 @@ public: return; } - llvm::StringRef WarningName(Literal.GetString(), Literal.GetStringLength()); + llvm::StringRef WarningName(Literal.GetString()); if (WarningName.size() < 3 || WarningName[0] != '-' || WarningName[1] != 'W') { @@ -919,6 +923,8 @@ public: Map, DiagLoc)) PP.Diag(StrToks[0].getLocation(), diag::warn_pragma_diagnostic_unknown_warning) << WarningName; + else if (Callbacks) + Callbacks->PragmaDiagnostic(DiagLoc, Namespace, Map, WarningName); } }; @@ -1013,13 +1019,13 @@ void Preprocessor::RegisterBuiltinPragmas() { AddPragmaHandler("GCC", new PragmaPoisonHandler()); AddPragmaHandler("GCC", new PragmaSystemHeaderHandler()); AddPragmaHandler("GCC", new PragmaDependencyHandler()); - AddPragmaHandler("GCC", new PragmaDiagnosticHandler()); + AddPragmaHandler("GCC", new PragmaDiagnosticHandler("GCC")); // #pragma clang ... AddPragmaHandler("clang", new PragmaPoisonHandler()); AddPragmaHandler("clang", new PragmaSystemHeaderHandler()); AddPragmaHandler("clang", new PragmaDebugHandler()); AddPragmaHandler("clang", new PragmaDependencyHandler()); - AddPragmaHandler("clang", new PragmaDiagnosticHandler()); + AddPragmaHandler("clang", new PragmaDiagnosticHandler("clang")); AddPragmaHandler("STDC", new PragmaSTDC_FENV_ACCESSHandler()); AddPragmaHandler("STDC", new PragmaSTDC_CX_LIMITED_RANGEHandler()); diff --git a/lib/Lex/PreprocessingRecord.cpp b/lib/Lex/PreprocessingRecord.cpp index 0c8d948ce0a7..9f93ab04502a 100644 --- a/lib/Lex/PreprocessingRecord.cpp +++ b/lib/Lex/PreprocessingRecord.cpp @@ -45,8 +45,8 @@ void PreprocessingRecord::MaybeLoadPreallocatedEntities() const { ExternalSource->ReadPreprocessedEntities(); } -PreprocessingRecord::PreprocessingRecord(bool IncludeNestedMacroInstantiations) - : IncludeNestedMacroInstantiations(IncludeNestedMacroInstantiations), +PreprocessingRecord::PreprocessingRecord(bool IncludeNestedMacroExpansions) + : IncludeNestedMacroExpansions(IncludeNestedMacroExpansions), ExternalSource(0), NumPreallocatedEntities(0), LoadedPreallocatedEntities(false) { @@ -121,14 +121,13 @@ MacroDefinition *PreprocessingRecord::findMacroDefinition(const MacroInfo *MI) { } void PreprocessingRecord::MacroExpands(const Token &Id, const MacroInfo* MI) { - if (!IncludeNestedMacroInstantiations && Id.getLocation().isMacroID()) + if (!IncludeNestedMacroExpansions && Id.getLocation().isMacroID()) return; if (MacroDefinition *Def = findMacroDefinition(MI)) PreprocessedEntities.push_back( - new (*this) MacroInstantiation(Id.getIdentifierInfo(), - Id.getLocation(), - Def)); + new (*this) MacroExpansion(Id.getIdentifierInfo(), + Id.getLocation(), Def)); } void PreprocessingRecord::MacroDefined(const Token &Id, diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index fdc18f878180..e7aa286a16bf 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -118,6 +118,8 @@ Preprocessor::Preprocessor(Diagnostic &diags, const LangOptions &opts, Preprocessor::~Preprocessor() { assert(BacktrackPositions.empty() && "EnableBacktrack/Backtrack imbalance!"); + assert(MacroExpandingLexersStack.empty() && MacroExpandedTokens.empty() && + "Preprocessor::HandleEndOfTokenLexer should have cleared those"); while (!IncludeMacroStack.empty()) { delete IncludeMacroStack.back().TheLexer; @@ -225,6 +227,10 @@ Preprocessor::macro_begin(bool IncludeExternalMacros) const { return Macros.begin(); } +size_t Preprocessor::getTotalMemory() const { + return BP.getTotalMemory() + MacroExpandedTokens.capacity()*sizeof(Token); +} + Preprocessor::macro_iterator Preprocessor::macro_end(bool IncludeExternalMacros) const { if (IncludeExternalMacros && ExternalSource && @@ -322,15 +328,15 @@ llvm::StringRef Preprocessor::getSpelling(const Token &Tok, /// location for it. If specified, the source location provides a source /// location for the token. void Preprocessor::CreateString(const char *Buf, unsigned Len, Token &Tok, - SourceLocation InstantiationLoc) { + SourceLocation ExpansionLoc) { Tok.setLength(Len); const char *DestPtr; SourceLocation Loc = ScratchBuf->getToken(Buf, Len, DestPtr); - if (InstantiationLoc.isValid()) - Loc = SourceMgr.createInstantiationLoc(Loc, InstantiationLoc, - InstantiationLoc, Len); + if (ExpansionLoc.isValid()) + Loc = SourceMgr.createInstantiationLoc(Loc, ExpansionLoc, + ExpansionLoc, Len); Tok.setLocation(Loc); // If this is a raw identifier or a literal token, set the pointer data. @@ -528,10 +534,10 @@ CommentHandler::~CommentHandler() { } CodeCompletionHandler::~CodeCompletionHandler() { } void Preprocessor::createPreprocessingRecord( - bool IncludeNestedMacroInstantiations) { + bool IncludeNestedMacroExpansions) { if (Record) return; - Record = new PreprocessingRecord(IncludeNestedMacroInstantiations); + Record = new PreprocessingRecord(IncludeNestedMacroExpansions); addPPCallbacks(Record); } diff --git a/lib/Lex/TokenLexer.cpp b/lib/Lex/TokenLexer.cpp index 65aff0d1d037..8ff82f160033 100644 --- a/lib/Lex/TokenLexer.cpp +++ b/lib/Lex/TokenLexer.cpp @@ -23,7 +23,7 @@ using namespace clang; /// Create a TokenLexer for the specified macro with the specified actual /// arguments. Note that this ctor takes ownership of the ActualArgs pointer. -void TokenLexer::Init(Token &Tok, SourceLocation ILEnd, MacroArgs *Actuals) { +void TokenLexer::Init(Token &Tok, SourceLocation ELEnd, MacroArgs *Actuals) { // If the client is reusing a TokenLexer, make sure to free any memory // associated with it. destroy(); @@ -32,14 +32,36 @@ void TokenLexer::Init(Token &Tok, SourceLocation ILEnd, MacroArgs *Actuals) { ActualArgs = Actuals; CurToken = 0; - InstantiateLocStart = Tok.getLocation(); - InstantiateLocEnd = ILEnd; + ExpandLocStart = Tok.getLocation(); + ExpandLocEnd = ELEnd; AtStartOfLine = Tok.isAtStartOfLine(); HasLeadingSpace = Tok.hasLeadingSpace(); Tokens = &*Macro->tokens_begin(); OwnsTokens = false; DisableMacroExpansion = false; NumTokens = Macro->tokens_end()-Macro->tokens_begin(); + MacroExpansionStart = SourceLocation(); + + SourceManager &SM = PP.getSourceManager(); + MacroStartSLocOffset = SM.getNextOffset(); + + if (NumTokens > 0) { + assert(Tokens[0].getLocation().isValid()); + assert((Tokens[0].getLocation().isFileID() || Tokens[0].is(tok::comment)) && + "Macro defined in macro?"); + assert(ExpandLocStart.isValid()); + + // Reserve a source location entry chunk for the length of the macro + // definition. Tokens that get lexed directly from the definition will + // have their locations pointing inside this chunk. This is to avoid + // creating separate source location entries for each token. + SourceLocation macroStart = SM.getInstantiationLoc(Tokens[0].getLocation()); + MacroDefStartInfo = SM.getDecomposedLoc(macroStart); + MacroExpansionStart = SM.createInstantiationLoc(macroStart, + ExpandLocStart, + ExpandLocEnd, + Macro->getDefinitionLength(SM)); + } // If this is a function-like macro, expand the arguments and change // Tokens to point to the expanded tokens. @@ -69,9 +91,10 @@ void TokenLexer::Init(const Token *TokArray, unsigned NumToks, DisableMacroExpansion = disableMacroExpansion; NumTokens = NumToks; CurToken = 0; - InstantiateLocStart = InstantiateLocEnd = SourceLocation(); + ExpandLocStart = ExpandLocEnd = SourceLocation(); AtStartOfLine = false; HasLeadingSpace = false; + MacroExpansionStart = SourceLocation(); // Set HasLeadingSpace/AtStartOfLine so that the first token will be // returned unmodified. @@ -98,6 +121,8 @@ void TokenLexer::destroy() { /// Expand the arguments of a function-like macro so that we can quickly /// return preexpanded tokens from Tokens. void TokenLexer::ExpandFunctionArguments() { + SourceManager &SM = PP.getSourceManager(); + llvm::SmallVector<Token, 128> ResultToks; // Loop through 'Tokens', expanding them into ResultToks. Keep @@ -119,13 +144,19 @@ void TokenLexer::ExpandFunctionArguments() { int ArgNo = Macro->getArgumentNum(Tokens[i+1].getIdentifierInfo()); assert(ArgNo != -1 && "Token following # is not an argument?"); + SourceLocation hashInstLoc; + if(ExpandLocStart.isValid()) { + hashInstLoc = getMacroExpansionLocation(CurTok.getLocation()); + assert(hashInstLoc.isValid() && "Expected '#' to come from definition"); + } + Token Res; if (CurTok.is(tok::hash)) // Stringify - Res = ActualArgs->getStringifiedArgument(ArgNo, PP); + Res = ActualArgs->getStringifiedArgument(ArgNo, PP, hashInstLoc); else { // 'charify': don't bother caching these. Res = MacroArgs::StringifyArgument(ActualArgs->getUnexpArgument(ArgNo), - PP, true); + PP, true, hashInstLoc); } // The stringified/charified string leading space flag gets set to match @@ -185,6 +216,27 @@ void TokenLexer::ExpandFunctionArguments() { unsigned NumToks = MacroArgs::getArgLength(ResultArgToks); ResultToks.append(ResultArgToks, ResultArgToks+NumToks); + // If the '##' came from expanding an argument, turn it into 'unknown' + // to avoid pasting. + for (unsigned i = FirstResult, e = ResultToks.size(); i != e; ++i) { + Token &Tok = ResultToks[i]; + if (Tok.is(tok::hashhash)) + Tok.setKind(tok::unknown); + } + + if(ExpandLocStart.isValid()) { + SourceLocation curInst = + getMacroExpansionLocation(CurTok.getLocation()); + assert(curInst.isValid() && + "Expected arg identifier to come from definition"); + for (unsigned i = FirstResult, e = ResultToks.size(); i != e; ++i) { + Token &Tok = ResultToks[i]; + Tok.setLocation(SM.createMacroArgInstantiationLoc(Tok.getLocation(), + curInst, + Tok.getLength())); + } + } + // If any tokens were substituted from the argument, the whitespace // before the first token should match the whitespace of the arg // identifier. @@ -220,6 +272,29 @@ void TokenLexer::ExpandFunctionArguments() { ResultToks.append(ArgToks, ArgToks+NumToks); + // If the '##' came from expanding an argument, turn it into 'unknown' + // to avoid pasting. + for (unsigned i = ResultToks.size() - NumToks, e = ResultToks.size(); + i != e; ++i) { + Token &Tok = ResultToks[i]; + if (Tok.is(tok::hashhash)) + Tok.setKind(tok::unknown); + } + + if (ExpandLocStart.isValid()) { + SourceLocation curInst = + getMacroExpansionLocation(CurTok.getLocation()); + assert(curInst.isValid() && + "Expected arg identifier to come from definition"); + for (unsigned i = ResultToks.size() - NumToks, e = ResultToks.size(); + i != e; ++i) { + Token &Tok = ResultToks[i]; + Tok.setLocation(SM.createMacroArgInstantiationLoc(Tok.getLocation(), + curInst, + Tok.getLength())); + } + } + // If this token (the macro argument) was supposed to get leading // whitespace, transfer this information onto the first token of the // expansion. @@ -284,15 +359,11 @@ void TokenLexer::ExpandFunctionArguments() { assert(!OwnsTokens && "This would leak if we already own the token list"); // This is deleted in the dtor. NumTokens = ResultToks.size(); - llvm::BumpPtrAllocator &Alloc = PP.getPreprocessorAllocator(); - Token *Res = - static_cast<Token *>(Alloc.Allocate(sizeof(Token)*ResultToks.size(), - llvm::alignOf<Token>())); - if (NumTokens) - memcpy(Res, &ResultToks[0], NumTokens*sizeof(Token)); - Tokens = Res; - - // The preprocessor bump pointer owns these tokens, not us. + // The tokens will be added to Preprocessor's cache and will be removed + // when this TokenLexer finishes lexing them. + Tokens = PP.cacheMacroExpandedTokens(this, ResultToks); + + // The preprocessor cache of macro expanded tokens owns these tokens,not us. OwnsTokens = false; } } @@ -317,6 +388,8 @@ void TokenLexer::Lex(Token &Tok) { return PPCache.Lex(Tok); } + SourceManager &SM = PP.getSourceManager(); + // If this is the first token of the expanded result, we inherit spacing // properties later. bool isFirstToken = CurToken == 0; @@ -327,7 +400,8 @@ void TokenLexer::Lex(Token &Tok) { bool TokenIsFromPaste = false; // If this token is followed by a token paste (##) operator, paste the tokens! - if (!isAtEnd() && Tokens[CurToken].is(tok::hashhash)) { + // Note that ## is a normal token when not expanding a macro. + if (!isAtEnd() && Tokens[CurToken].is(tok::hashhash) && Macro) { // When handling the microsoft /##/ extension, the final token is // returned by PasteTokens, not the pasted token. if (PasteTokens(Tok)) @@ -339,14 +413,25 @@ void TokenLexer::Lex(Token &Tok) { // The token's current location indicate where the token was lexed from. We // need this information to compute the spelling of the token, but any // diagnostics for the expanded token should appear as if they came from - // InstantiationLoc. Pull this information together into a new SourceLocation + // ExpansionLoc. Pull this information together into a new SourceLocation // that captures all of this. - if (InstantiateLocStart.isValid()) { // Don't do this for token streams. - SourceManager &SM = PP.getSourceManager(); - Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(), - InstantiateLocStart, - InstantiateLocEnd, - Tok.getLength())); + if (ExpandLocStart.isValid() && // Don't do this for token streams. + // Check that the token's location was not already set properly. + SM.isBeforeInSourceLocationOffset(Tok.getLocation(), + MacroStartSLocOffset)) { + SourceLocation instLoc; + if (Tok.is(tok::comment)) { + instLoc = SM.createInstantiationLoc(Tok.getLocation(), + ExpandLocStart, + ExpandLocEnd, + Tok.getLength()); + } else { + instLoc = getMacroExpansionLocation(Tok.getLocation()); + assert(instLoc.isValid() && + "Location for token not coming from definition was not set!"); + } + + Tok.setLocation(instLoc); } // If this is the first token, set the lexical properties of the token to @@ -384,9 +469,10 @@ void TokenLexer::Lex(Token &Tok) { bool TokenLexer::PasteTokens(Token &Tok) { llvm::SmallString<128> Buffer; const char *ResultTokStrPtr = 0; + SourceLocation PasteOpLoc; do { // Consume the ## operator. - SourceLocation PasteOpLoc = Tokens[CurToken].getLocation(); + PasteOpLoc = Tokens[CurToken].getLocation(); ++CurToken; assert(!isAtEnd() && "No token on the RHS of a paste operator!"); @@ -484,12 +570,12 @@ bool TokenLexer::PasteTokens(Token &Tok) { // Do not emit the error when preprocessing assembler code. if (!PP.getLangOptions().AsmPreprocessor) { - // Explicitly convert the token location to have proper instantiation + // Explicitly convert the token location to have proper expansion // information so that the user knows where it came from. SourceManager &SM = PP.getSourceManager(); SourceLocation Loc = - SM.createInstantiationLoc(PasteOpLoc, InstantiateLocStart, - InstantiateLocEnd, 2); + SM.createInstantiationLoc(PasteOpLoc, ExpandLocStart, + ExpandLocEnd, 2); // If we're in microsoft extensions mode, downgrade this from a hard // error to a warning that defaults to an error. This allows // disabling it. @@ -512,12 +598,30 @@ bool TokenLexer::PasteTokens(Token &Tok) { // Transfer properties of the LHS over the the Result. Result.setFlagValue(Token::StartOfLine , Tok.isAtStartOfLine()); Result.setFlagValue(Token::LeadingSpace, Tok.hasLeadingSpace()); - + // Finally, replace LHS with the result, consume the RHS, and iterate. ++CurToken; Tok = Result; } while (!isAtEnd() && Tokens[CurToken].is(tok::hashhash)); + // The token's current location indicate where the token was lexed from. We + // need this information to compute the spelling of the token, but any + // diagnostics for the expanded token should appear as if the token was + // expanded from the (##) operator. Pull this information together into + // a new SourceLocation that captures all of this. + if (ExpandLocStart.isValid()) { + SourceManager &SM = PP.getSourceManager(); + SourceLocation pasteLocInst = + getMacroExpansionLocation(PasteOpLoc); + assert(pasteLocInst.isValid() && + "Expected '##' to come from definition"); + + Tok.setLocation(SM.createInstantiationLoc(Tok.getLocation(), + pasteLocInst, + pasteLocInst, + Tok.getLength())); + } + // Now that we got the result token, it will be subject to expansion. Since // token pasting re-lexes the result token in raw mode, identifier information // isn't looked up. As such, if the result is an identifier, look up id info. @@ -548,7 +652,7 @@ bool TokenLexer::isParsingPreprocessorDirective() const { /// HandleMicrosoftCommentPaste - In microsoft compatibility mode, /##/ pastes /// together to form a comment that comments out everything in the current /// macro, other active macros, and anything left on the current physical -/// source line of the instantiated buffer. Handle this by returning the +/// source line of the expanded buffer. Handle this by returning the /// first token on the next line. void TokenLexer::HandleMicrosoftCommentPaste(Token &Tok) { // We 'comment out' the rest of this macro by just ignoring the rest of the @@ -561,3 +665,23 @@ void TokenLexer::HandleMicrosoftCommentPaste(Token &Tok) { PP.HandleMicrosoftCommentPaste(Tok); } + +/// \brief If \arg loc is a FileID and points inside the current macro +/// definition, returns the appropriate source location pointing at the +/// macro expansion source location entry. +SourceLocation TokenLexer::getMacroExpansionLocation(SourceLocation loc) const { + assert(ExpandLocStart.isValid() && MacroExpansionStart.isValid() && + "Not appropriate for token streams"); + assert(loc.isValid()); + + SourceManager &SM = PP.getSourceManager(); + unsigned relativeOffset; + if (loc.isFileID() && + SM.isInFileID(loc, + MacroDefStartInfo.first, MacroDefStartInfo.second, + Macro->getDefinitionLength(SM), &relativeOffset)) { + return MacroExpansionStart.getFileLocWithOffset(relativeOffset); + } + + return SourceLocation(); +} diff --git a/lib/Makefile b/lib/Makefile index 924819c81807..74df7abcef24 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,8 +9,8 @@ CLANG_LEVEL := .. PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \ - StaticAnalyzer Rewrite Serialization Frontend FrontendTool \ - Index Driver + StaticAnalyzer Rewrite ARCMigrate Serialization Frontend \ + FrontendTool Index Driver include $(CLANG_LEVEL)/Makefile diff --git a/lib/Parse/ParseAST.cpp b/lib/Parse/ParseAST.cpp index 21917b23ffb6..56584c96180b 100644 --- a/lib/Parse/ParseAST.cpp +++ b/lib/Parse/ParseAST.cpp @@ -57,6 +57,10 @@ void clang::ParseAST(Sema &S, bool PrintStats) { Stmt::CollectingStats(true); } + // Also turn on collection of stats inside of the Sema object. + bool OldCollectStats = PrintStats; + std::swap(OldCollectStats, S.CollectStats); + ASTConsumer *Consumer = &S.getASTConsumer(); llvm::OwningPtr<Parser> ParseOP(new Parser(S.getPreprocessor(), S)); @@ -95,9 +99,10 @@ void clang::ParseAST(Sema &S, bool PrintStats) { Consumer->HandleTopLevelDecl(DeclGroupRef(*I)); Consumer->HandleTranslationUnit(S.getASTContext()); - + + std::swap(OldCollectStats, S.CollectStats); if (PrintStats) { - fprintf(stderr, "\nSTATISTICS:\n"); + llvm::errs() << "\nSTATISTICS:\n"; P.getActions().PrintStats(); S.getASTContext().PrintStats(); Decl::PrintStats(); diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp index ad3fcfe0d369..0e17295858b9 100644 --- a/lib/Parse/ParseDecl.cpp +++ b/lib/Parse/ParseDecl.cpp @@ -31,10 +31,16 @@ using namespace clang; /// /// Called type-id in C++. TypeResult Parser::ParseTypeName(SourceRange *Range, - Declarator::TheContext Context) { + Declarator::TheContext Context, + ObjCDeclSpec *objcQuals, + AccessSpecifier AS, + Decl **OwnedType) { // Parse the common declaration-specifiers piece. DeclSpec DS(AttrFactory); - ParseSpecifierQualifierList(DS); + DS.setObjCQualifiers(objcQuals); + ParseSpecifierQualifierList(DS, AS); + if (OwnedType) + *OwnedType = DS.isTypeSpecOwned() ? DS.getRepAsDecl() : 0; // Parse the abstract-declarator, if present. Declarator DeclaratorInfo(DS, Context); @@ -672,6 +678,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, ParenBraceBracketBalancer BalancerRAIIObj(*this); Decl *SingleDecl = 0; + Decl *OwnedType = 0; switch (Tok.getKind()) { case tok::kw_template: case tok::kw_export: @@ -694,7 +701,7 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, break; case tok::kw_using: SingleDecl = ParseUsingDirectiveOrDeclaration(Context, ParsedTemplateInfo(), - DeclEnd, attrs); + DeclEnd, attrs, &OwnedType); break; case tok::kw_static_assert: case tok::kw__Static_assert: @@ -706,8 +713,9 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(StmtVector &Stmts, } // This routine returns a DeclGroup, if the thing we parsed only contains a - // single decl, convert it now. - return Actions.ConvertDeclToDeclGroup(SingleDecl); + // single decl, convert it now. Alias declarations can also declare a type; + // include that too if it is present. + return Actions.ConvertDeclToDeclGroup(SingleDecl, OwnedType); } /// simple-declaration: [C99 6.7: declaration] [C++ 7p1: dcl.dcl] @@ -1079,10 +1087,10 @@ Decl *Parser::ParseDeclarationAfterDeclaratorAndAttributes(Declarator &D, /// type-qualifier specifier-qualifier-list[opt] /// [GNU] attributes specifier-qualifier-list[opt] /// -void Parser::ParseSpecifierQualifierList(DeclSpec &DS) { +void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS) { /// specifier-qualifier-list is a subset of declaration-specifiers. Just /// parse declaration-specifiers and complain about extra stuff. - ParseDeclarationSpecifiers(DS); + ParseDeclarationSpecifiers(DS, ParsedTemplateInfo(), AS); // Validate declspec for type-name. unsigned Specs = DS.getParsedSpecifiers(); @@ -1394,8 +1402,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // Thus, if the template-name is actually the constructor // name, then the code is ill-formed; this interpretation is // reinforced by the NAD status of core issue 635. - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next); if ((DSContext == DSC_top_level || (DSContext == DSC_class && DS.isFriendSpecified())) && TemplateId->Name && @@ -1597,8 +1604,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // type-name case tok::annot_template_id: { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind != TNK_Type_template) { // This template-id does not refer to a type name, so we're // done with the type-specifiers. @@ -2444,13 +2450,29 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, Actions.CodeCompleteTag(getCurScope(), DeclSpec::TST_enum); ConsumeCodeCompletionToken(); } + + bool IsScopedEnum = false; + bool IsScopedUsingClassTag = false; + + if (getLang().CPlusPlus0x && + (Tok.is(tok::kw_class) || Tok.is(tok::kw_struct))) { + IsScopedEnum = true; + IsScopedUsingClassTag = Tok.is(tok::kw_class); + ConsumeToken(); + } // If attributes exist after tag, parse them. ParsedAttributes attrs(AttrFactory); MaybeParseGNUAttributes(attrs); + bool AllowFixedUnderlyingType = getLang().CPlusPlus0x || getLang().Microsoft; + CXXScopeSpec &SS = DS.getTypeSpecScope(); if (getLang().CPlusPlus) { + // "enum foo : bar;" is not a potential typo for "enum foo::bar;" + // if a fixed underlying type is allowed. + ColonProtectionRAIIObject X(*this, AllowFixedUnderlyingType); + if (ParseOptionalCXXScopeSpecifier(SS, ParsedType(), false)) return; @@ -2465,17 +2487,6 @@ void Parser::ParseEnumSpecifier(SourceLocation StartLoc, DeclSpec &DS, } } - bool AllowFixedUnderlyingType = getLang().CPlusPlus0x || getLang().Microsoft; - bool IsScopedEnum = false; - bool IsScopedUsingClassTag = false; - - if (getLang().CPlusPlus0x && - (Tok.is(tok::kw_class) || Tok.is(tok::kw_struct))) { - IsScopedEnum = true; - IsScopedUsingClassTag = Tok.is(tok::kw_class); - ConsumeToken(); - } - // Must have either 'enum name' or 'enum {...}'. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::l_brace) && (AllowFixedUnderlyingType && Tok.isNot(tok::colon))) { @@ -3016,6 +3027,10 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) { case tok::kw___attribute: return true; + // C++0x decltype. + case tok::kw_decltype: + return true; + // GNU ObjC bizarre protocol extension: <proto1,proto2> with implicit 'id'. case tok::less: return getLang().ObjC1; @@ -3639,33 +3654,15 @@ void Parser::ParseParenDeclarator(Declarator &D) { /// declarator D up to a paren, which indicates that we are parsing function /// arguments. /// -/// If AttrList is non-null, then the caller parsed those arguments immediately +/// If attrs is non-null, then the caller parsed those arguments immediately /// after the open paren - they should be considered to be the first argument of /// a parameter. If RequiresArg is true, then the first argument of the /// function is required to be present and required to not be an identifier /// list. /// -/// This method also handles this portion of the grammar: -/// parameter-type-list: [C99 6.7.5] -/// parameter-list -/// parameter-list ',' '...' -/// [C++] parameter-list '...' -/// -/// parameter-list: [C99 6.7.5] -/// parameter-declaration -/// parameter-list ',' parameter-declaration -/// -/// parameter-declaration: [C99 6.7.5] -/// declaration-specifiers declarator -/// [C++] declaration-specifiers declarator '=' assignment-expression -/// [GNU] declaration-specifiers declarator attributes -/// declaration-specifiers abstract-declarator[opt] -/// [C++] declaration-specifiers abstract-declarator[opt] -/// '=' assignment-expression -/// [GNU] declaration-specifiers abstract-declarator[opt] attributes -/// -/// For C++, after the parameter-list, it also parses "cv-qualifier-seq[opt]", -/// C++0x "ref-qualifier[opt]" and "exception-specification[opt]". +/// For C++, after the parameter-list, it also parses cv-qualifier-seq[opt], +/// (C++0x) ref-qualifier[opt], exception-specification[opt], and +/// (C++0x) trailing-return-type[opt]. /// /// [C++0x] exception-specification: /// dynamic-exception-specification @@ -3677,36 +3674,62 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, // lparen is already consumed! assert(D.isPastIdentifier() && "Should not call before identifier!"); + // This should be true when the function has typed arguments. + // Otherwise, it is treated as a K&R-style function. + bool HasProto = false; + // Build up an array of information about the parsed arguments. + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo; + // Remember where we see an ellipsis, if any. + SourceLocation EllipsisLoc; + + DeclSpec DS(AttrFactory); + bool RefQualifierIsLValueRef = true; + SourceLocation RefQualifierLoc; + ExceptionSpecificationType ESpecType = EST_None; + SourceRange ESpecRange; + llvm::SmallVector<ParsedType, 2> DynamicExceptions; + llvm::SmallVector<SourceRange, 2> DynamicExceptionRanges; + ExprResult NoexceptExpr; ParsedType TrailingReturnType; + + SourceLocation EndLoc; - // This parameter list may be empty. - if (Tok.is(tok::r_paren)) { + if (isFunctionDeclaratorIdentifierList()) { if (RequiresArg) Diag(Tok, diag::err_argument_required_after_attribute); - SourceLocation EndLoc = ConsumeParen(); // Eat the closing ')'. + ParseFunctionDeclaratorIdentifierList(D, ParamInfo); + + EndLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); + } else { + // Enter function-declaration scope, limiting any declarators to the + // function prototype scope, including parameter declarators. + ParseScope PrototypeScope(this, + Scope::FunctionPrototypeScope|Scope::DeclScope); + + if (Tok.isNot(tok::r_paren)) + ParseParameterDeclarationClause(D, attrs, ParamInfo, EllipsisLoc); + else if (RequiresArg) + Diag(Tok, diag::err_argument_required_after_attribute); + + HasProto = ParamInfo.size() || getLang().CPlusPlus; + + // If we have the closing ')', eat it. + EndLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); - // cv-qualifier-seq[opt]. - DeclSpec DS(AttrFactory); - SourceLocation RefQualifierLoc; - bool RefQualifierIsLValueRef = true; - ExceptionSpecificationType ESpecType = EST_None; - SourceRange ESpecRange; - llvm::SmallVector<ParsedType, 2> DynamicExceptions; - llvm::SmallVector<SourceRange, 2> DynamicExceptionRanges; - ExprResult NoexceptExpr; if (getLang().CPlusPlus) { MaybeParseCXX0XAttributes(attrs); + // Parse cv-qualifier-seq[opt]. ParseTypeQualifierListOpt(DS, false /*no attributes*/); - if (!DS.getSourceRange().getEnd().isInvalid()) - EndLoc = DS.getSourceRange().getEnd(); + if (!DS.getSourceRange().getEnd().isInvalid()) + EndLoc = DS.getSourceRange().getEnd(); - // Parse ref-qualifier[opt] + // Parse ref-qualifier[opt]. if (Tok.is(tok::amp) || Tok.is(tok::ampamp)) { if (!getLang().CPlusPlus0x) Diag(Tok, diag::ext_ref_qualifier); - + RefQualifierIsLValueRef = Tok.is(tok::amp); RefQualifierLoc = ConsumeToken(); EndLoc = RefQualifierLoc; @@ -3720,87 +3743,158 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, if (ESpecType != EST_None) EndLoc = ESpecRange.getEnd(); - // Parse trailing-return-type. + // Parse trailing-return-type[opt]. if (getLang().CPlusPlus0x && Tok.is(tok::arrow)) { TrailingReturnType = ParseTrailingReturnType().get(); } } - // Remember that we parsed a function type, and remember the attributes. - // int() -> no prototype, no '...'. - D.AddTypeInfo(DeclaratorChunk::getFunction(/*prototype*/getLang().CPlusPlus, - /*variadic*/ false, - SourceLocation(), - /*arglist*/ 0, 0, - DS.getTypeQualifiers(), - RefQualifierIsLValueRef, - RefQualifierLoc, - ESpecType, ESpecRange.getBegin(), - DynamicExceptions.data(), - DynamicExceptionRanges.data(), - DynamicExceptions.size(), - NoexceptExpr.isUsable() ? - NoexceptExpr.get() : 0, - LParenLoc, EndLoc, D, - TrailingReturnType), - attrs, EndLoc); - return; + // Leave prototype scope. + PrototypeScope.Exit(); } - // Alternatively, this parameter list may be an identifier list form for a - // K&R-style function: void foo(a,b,c) - if (!getLang().CPlusPlus && Tok.is(tok::identifier) - && !TryAltiVecVectorToken()) { - if (TryAnnotateTypeOrScopeToken() || !Tok.is(tok::annot_typename)) { - // K&R identifier lists can't have typedefs as identifiers, per - // C99 6.7.5.3p11. - if (RequiresArg) - Diag(Tok, diag::err_argument_required_after_attribute); - - // Identifier list. Note that '(' identifier-list ')' is only allowed for - // normal declarators, not for abstract-declarators. Get the first - // identifier. - Token FirstTok = Tok; - ConsumeToken(); // eat the first identifier. - - // Identifier lists follow a really simple grammar: the identifiers can - // be followed *only* by a ", moreidentifiers" or ")". However, K&R - // identifier lists are really rare in the brave new modern world, and it - // is very common for someone to typo a type in a non-k&r style list. If - // we are presented with something like: "void foo(intptr x, float y)", - // we don't want to start parsing the function declarator as though it is - // a K&R style declarator just because intptr is an invalid type. - // - // To handle this, we check to see if the token after the first identifier - // is a "," or ")". Only if so, do we parse it as an identifier list. - if (Tok.is(tok::comma) || Tok.is(tok::r_paren)) - return ParseFunctionDeclaratorIdentifierList(LParenLoc, - FirstTok.getIdentifierInfo(), - FirstTok.getLocation(), D); - - // If we get here, the code is invalid. Push the first identifier back - // into the token stream and parse the first argument as an (invalid) - // normal argument declarator. - PP.EnterToken(Tok); - Tok = FirstTok; + // Remember that we parsed a function type, and remember the attributes. + D.AddTypeInfo(DeclaratorChunk::getFunction(HasProto, + /*isVariadic=*/EllipsisLoc.isValid(), + EllipsisLoc, + ParamInfo.data(), ParamInfo.size(), + DS.getTypeQualifiers(), + RefQualifierIsLValueRef, + RefQualifierLoc, + /*MutableLoc=*/SourceLocation(), + ESpecType, ESpecRange.getBegin(), + DynamicExceptions.data(), + DynamicExceptionRanges.data(), + DynamicExceptions.size(), + NoexceptExpr.isUsable() ? + NoexceptExpr.get() : 0, + LParenLoc, EndLoc, D, + TrailingReturnType), + attrs, EndLoc); +} + +/// isFunctionDeclaratorIdentifierList - This parameter list may have an +/// identifier list form for a K&R-style function: void foo(a,b,c) +/// +/// Note that identifier-lists are only allowed for normal declarators, not for +/// abstract-declarators. +bool Parser::isFunctionDeclaratorIdentifierList() { + return !getLang().CPlusPlus + && Tok.is(tok::identifier) + && !TryAltiVecVectorToken() + // K&R identifier lists can't have typedefs as identifiers, per C99 + // 6.7.5.3p11. + && (TryAnnotateTypeOrScopeToken() || !Tok.is(tok::annot_typename)) + // Identifier lists follow a really simple grammar: the identifiers can + // be followed *only* by a ", identifier" or ")". However, K&R + // identifier lists are really rare in the brave new modern world, and + // it is very common for someone to typo a type in a non-K&R style + // list. If we are presented with something like: "void foo(intptr x, + // float y)", we don't want to start parsing the function declarator as + // though it is a K&R style declarator just because intptr is an + // invalid type. + // + // To handle this, we check to see if the token after the first + // identifier is a "," or ")". Only then do we parse it as an + // identifier list. + && (NextToken().is(tok::comma) || NextToken().is(tok::r_paren)); +} + +/// ParseFunctionDeclaratorIdentifierList - While parsing a function declarator +/// we found a K&R-style identifier list instead of a typed parameter list. +/// +/// After returning, ParamInfo will hold the parsed parameters. +/// +/// identifier-list: [C99 6.7.5] +/// identifier +/// identifier-list ',' identifier +/// +void Parser::ParseFunctionDeclaratorIdentifierList( + Declarator &D, + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> &ParamInfo) { + // If there was no identifier specified for the declarator, either we are in + // an abstract-declarator, or we are in a parameter declarator which was found + // to be abstract. In abstract-declarators, identifier lists are not valid: + // diagnose this. + if (!D.getIdentifier()) + Diag(Tok, diag::ext_ident_list_in_param); + + // Maintain an efficient lookup of params we have seen so far. + llvm::SmallSet<const IdentifierInfo*, 16> ParamsSoFar; + + while (1) { + // If this isn't an identifier, report the error and skip until ')'. + if (Tok.isNot(tok::identifier)) { + Diag(Tok, diag::err_expected_ident); + SkipUntil(tok::r_paren, /*StopAtSemi=*/true, /*DontConsume=*/true); + // Forget we parsed anything. + ParamInfo.clear(); + return; } - } - // Finally, a normal, non-empty parameter type list. + IdentifierInfo *ParmII = Tok.getIdentifierInfo(); - // Build up an array of information about the parsed arguments. - llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo; + // Reject 'typedef int y; int test(x, y)', but continue parsing. + if (Actions.getTypeName(*ParmII, Tok.getLocation(), getCurScope())) + Diag(Tok, diag::err_unexpected_typedef_ident) << ParmII; + + // Verify that the argument identifier has not already been mentioned. + if (!ParamsSoFar.insert(ParmII)) { + Diag(Tok, diag::err_param_redefinition) << ParmII; + } else { + // Remember this identifier in ParamInfo. + ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, + Tok.getLocation(), + 0)); + } + + // Eat the identifier. + ConsumeToken(); - // Enter function-declaration scope, limiting any declarators to the - // function prototype scope, including parameter declarators. - ParseScope PrototypeScope(this, - Scope::FunctionPrototypeScope|Scope::DeclScope); + // The list continues if we see a comma. + if (Tok.isNot(tok::comma)) + break; + ConsumeToken(); + } +} + +/// ParseParameterDeclarationClause - Parse a (possibly empty) parameter-list +/// after the opening parenthesis. This function will not parse a K&R-style +/// identifier list. +/// +/// D is the declarator being parsed. If attrs is non-null, then the caller +/// parsed those arguments immediately after the open paren - they should be +/// considered to be the first argument of a parameter. +/// +/// After returning, ParamInfo will hold the parsed parameters. EllipsisLoc will +/// be the location of the ellipsis, if any was parsed. +/// +/// parameter-type-list: [C99 6.7.5] +/// parameter-list +/// parameter-list ',' '...' +/// [C++] parameter-list '...' +/// +/// parameter-list: [C99 6.7.5] +/// parameter-declaration +/// parameter-list ',' parameter-declaration +/// +/// parameter-declaration: [C99 6.7.5] +/// declaration-specifiers declarator +/// [C++] declaration-specifiers declarator '=' assignment-expression +/// [GNU] declaration-specifiers declarator attributes +/// declaration-specifiers abstract-declarator[opt] +/// [C++] declaration-specifiers abstract-declarator[opt] +/// '=' assignment-expression +/// [GNU] declaration-specifiers abstract-declarator[opt] attributes +/// +void Parser::ParseParameterDeclarationClause( + Declarator &D, + ParsedAttributes &attrs, + llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> &ParamInfo, + SourceLocation &EllipsisLoc) { - bool IsVariadic = false; - SourceLocation EllipsisLoc; while (1) { if (Tok.is(tok::ellipsis)) { - IsVariadic = true; EllipsisLoc = ConsumeToken(); // Consume the ellipsis. break; } @@ -3817,6 +3911,11 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, // If the caller parsed attributes for the first argument, add them now. // Take them so that we only apply the attributes to the first parameter. + // FIXME: If we saw an ellipsis first, this code is not reached. Are the + // attributes lost? Should they even be allowed? + // FIXME: If we can leave the attributes in the token stream somehow, we can + // get rid of a parameter (attrs) and this statement. It might be too much + // hassle. DS.takeAttributesFrom(attrs); ParseDeclarationSpecifiers(DS); @@ -3912,7 +4011,6 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, // If the next token is a comma, consume it and keep reading arguments. if (Tok.isNot(tok::comma)) { if (Tok.is(tok::ellipsis)) { - IsVariadic = true; EllipsisLoc = ConsumeToken(); // Consume the ellipsis. if (!getLang().CPlusPlus) { @@ -3930,150 +4028,6 @@ void Parser::ParseFunctionDeclarator(SourceLocation LParenLoc, Declarator &D, ConsumeToken(); } - // If we have the closing ')', eat it. - SourceLocation EndLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); - - DeclSpec DS(AttrFactory); - SourceLocation RefQualifierLoc; - bool RefQualifierIsLValueRef = true; - ExceptionSpecificationType ESpecType = EST_None; - SourceRange ESpecRange; - llvm::SmallVector<ParsedType, 2> DynamicExceptions; - llvm::SmallVector<SourceRange, 2> DynamicExceptionRanges; - ExprResult NoexceptExpr; - - if (getLang().CPlusPlus) { - MaybeParseCXX0XAttributes(attrs); - - // Parse cv-qualifier-seq[opt]. - ParseTypeQualifierListOpt(DS, false /*no attributes*/); - if (!DS.getSourceRange().getEnd().isInvalid()) - EndLoc = DS.getSourceRange().getEnd(); - - // Parse ref-qualifier[opt] - if (Tok.is(tok::amp) || Tok.is(tok::ampamp)) { - if (!getLang().CPlusPlus0x) - Diag(Tok, diag::ext_ref_qualifier); - - RefQualifierIsLValueRef = Tok.is(tok::amp); - RefQualifierLoc = ConsumeToken(); - EndLoc = RefQualifierLoc; - } - - // FIXME: We should leave the prototype scope before parsing the exception - // specification, and then reenter it when parsing the trailing return type. - // FIXMEFIXME: Why? That wouldn't be right for the noexcept clause. - - // Parse exception-specification[opt]. - ESpecType = MaybeParseExceptionSpecification(ESpecRange, - DynamicExceptions, - DynamicExceptionRanges, - NoexceptExpr); - if (ESpecType != EST_None) - EndLoc = ESpecRange.getEnd(); - - // Parse trailing-return-type. - if (getLang().CPlusPlus0x && Tok.is(tok::arrow)) { - TrailingReturnType = ParseTrailingReturnType().get(); - } - } - - // Leave prototype scope. - PrototypeScope.Exit(); - - // Remember that we parsed a function type, and remember the attributes. - D.AddTypeInfo(DeclaratorChunk::getFunction(/*proto*/true, IsVariadic, - EllipsisLoc, - ParamInfo.data(), ParamInfo.size(), - DS.getTypeQualifiers(), - RefQualifierIsLValueRef, - RefQualifierLoc, - ESpecType, ESpecRange.getBegin(), - DynamicExceptions.data(), - DynamicExceptionRanges.data(), - DynamicExceptions.size(), - NoexceptExpr.isUsable() ? - NoexceptExpr.get() : 0, - LParenLoc, EndLoc, D, - TrailingReturnType), - attrs, EndLoc); -} - -/// ParseFunctionDeclaratorIdentifierList - While parsing a function declarator -/// we found a K&R-style identifier list instead of a type argument list. The -/// first identifier has already been consumed, and the current token is the -/// token right after it. -/// -/// identifier-list: [C99 6.7.5] -/// identifier -/// identifier-list ',' identifier -/// -void Parser::ParseFunctionDeclaratorIdentifierList(SourceLocation LParenLoc, - IdentifierInfo *FirstIdent, - SourceLocation FirstIdentLoc, - Declarator &D) { - // Build up an array of information about the parsed arguments. - llvm::SmallVector<DeclaratorChunk::ParamInfo, 16> ParamInfo; - llvm::SmallSet<const IdentifierInfo*, 16> ParamsSoFar; - - // If there was no identifier specified for the declarator, either we are in - // an abstract-declarator, or we are in a parameter declarator which was found - // to be abstract. In abstract-declarators, identifier lists are not valid: - // diagnose this. - if (!D.getIdentifier()) - Diag(FirstIdentLoc, diag::ext_ident_list_in_param); - - // The first identifier was already read, and is known to be the first - // identifier in the list. Remember this identifier in ParamInfo. - ParamsSoFar.insert(FirstIdent); - ParamInfo.push_back(DeclaratorChunk::ParamInfo(FirstIdent, FirstIdentLoc, 0)); - - while (Tok.is(tok::comma)) { - // Eat the comma. - ConsumeToken(); - - // If this isn't an identifier, report the error and skip until ')'. - if (Tok.isNot(tok::identifier)) { - Diag(Tok, diag::err_expected_ident); - SkipUntil(tok::r_paren); - return; - } - - IdentifierInfo *ParmII = Tok.getIdentifierInfo(); - - // Reject 'typedef int y; int test(x, y)', but continue parsing. - if (Actions.getTypeName(*ParmII, Tok.getLocation(), getCurScope())) - Diag(Tok, diag::err_unexpected_typedef_ident) << ParmII; - - // Verify that the argument identifier has not already been mentioned. - if (!ParamsSoFar.insert(ParmII)) { - Diag(Tok, diag::err_param_redefinition) << ParmII; - } else { - // Remember this identifier in ParamInfo. - ParamInfo.push_back(DeclaratorChunk::ParamInfo(ParmII, - Tok.getLocation(), - 0)); - } - - // Eat the identifier. - ConsumeToken(); - } - - // If we have the closing ')', eat it and we're done. - SourceLocation RLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); - - // Remember that we parsed a function type, and remember the attributes. This - // function type is always a K&R style function type, which is not varargs and - // has no prototype. - ParsedAttributes attrs(AttrFactory); - D.AddTypeInfo(DeclaratorChunk::getFunction(/*proto*/false, /*varargs*/false, - SourceLocation(), - &ParamInfo[0], ParamInfo.size(), - /*TypeQuals*/0, - true, SourceLocation(), - EST_None, SourceLocation(), 0, 0, - 0, 0, LParenLoc, RLoc, D), - attrs, RLoc); } /// [C90] direct-declarator '[' constant-expression[opt] ']' diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp index 51aa01091e73..172049c34053 100644 --- a/lib/Parse/ParseDeclCXX.cpp +++ b/lib/Parse/ParseDeclCXX.cpp @@ -314,7 +314,8 @@ Decl *Parser::ParseLinkage(ParsingDeclSpec &DS, unsigned Context) { Decl *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation &DeclEnd, - ParsedAttributesWithRange &attrs) { + ParsedAttributesWithRange &attrs, + Decl **OwnedType) { assert(Tok.is(tok::kw_using) && "Not using token"); // Eat 'using'. @@ -342,7 +343,8 @@ Decl *Parser::ParseUsingDirectiveOrDeclaration(unsigned Context, // Using declarations can't have attributes. ProhibitAttributes(attrs); - return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd); + return ParseUsingDeclaration(Context, TemplateInfo, UsingLoc, DeclEnd, + AS_none, OwnedType); } /// ParseUsingDirective - Parse C++ using-directive, assumes @@ -422,7 +424,8 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, const ParsedTemplateInfo &TemplateInfo, SourceLocation UsingLoc, SourceLocation &DeclEnd, - AccessSpecifier AS) { + AccessSpecifier AS, + Decl **OwnedType) { CXXScopeSpec SS; SourceLocation TypenameLoc; bool IsTypeName; @@ -511,7 +514,7 @@ Decl *Parser::ParseUsingDeclaration(unsigned Context, TypeAlias = ParseTypeName(0, TemplateInfo.Kind ? Declarator::AliasTemplateContext : - Declarator::AliasDeclContext); + Declarator::AliasDeclContext, 0, AS, OwnedType); } else // Parse (optional) attributes (most likely GNU strong-using extension). MaybeParseGNUAttributes(attrs); @@ -701,8 +704,7 @@ Parser::TypeResult Parser::ParseClassName(SourceLocation &EndLocation, CXXScopeSpec &SS) { // Check whether we have a template-id that names a type. if (Tok.is(tok::annot_template_id)) { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name) { AnnotateTemplateIdTokenAsType(); @@ -976,7 +978,7 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, } } } else if (Tok.is(tok::annot_template_id)) { - TemplateId = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateId = takeTemplateIdAnnotation(Tok); NameLoc = ConsumeToken(); if (TemplateId->Kind != TNK_Type_template && @@ -993,7 +995,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, DS.SetTypeSpecError(); SkipUntil(tok::semi, false, true); - TemplateId->Destroy(); if (SuppressingAccessChecks) Actions.ActOnStopSuppressingAccessChecks(); @@ -1051,9 +1052,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, } SkipUntil(tok::comma, true); - - if (TemplateId) - TemplateId->Destroy(); return; } @@ -1149,7 +1147,6 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, TemplateParams? &(*TemplateParams)[0] : 0, TemplateParams? TemplateParams->size() : 0)); } - TemplateId->Destroy(); } else if (TemplateInfo.Kind == ParsedTemplateInfo::ExplicitInstantiation && TUK == Sema::TUK_Declaration) { // Explicit instantiation of a member of a class template @@ -1294,6 +1291,11 @@ void Parser::ParseClassSpecifier(tok::TokenKind TagTokKind, break; } + // C++ [temp]p3 In a template-declaration which defines a class, no + // declarator is permitted. + if (TemplateInfo.Kind) + ExpectedSemi = true; + if (ExpectedSemi) { ExpectAndConsume(tok::semi, diag::err_expected_semi_after_tagdecl, TagType == DeclSpec::TST_class ? "class" @@ -1828,14 +1830,16 @@ void Parser::ParseCXXClassMemberDeclaration(AccessSpecifier AS, Diag(Tok, diag::err_bitfield_member_init); SkipUntil(tok::comma, true, true); } else { - HasDeferredInitializer = !DeclaratorInfo.isFunctionDeclarator() && + HasDeferredInitializer = !DeclaratorInfo.isDeclarationOfFunction() && + DeclaratorInfo.getDeclSpec().getStorageClassSpec() + != DeclSpec::SCS_static && DeclaratorInfo.getDeclSpec().getStorageClassSpec() - != DeclSpec::SCS_static; + != DeclSpec::SCS_typedef; if (!HasDeferredInitializer) { SourceLocation EqualLoc; Init = ParseCXXMemberInitializer( - DeclaratorInfo.isFunctionDeclarator(), EqualLoc); + DeclaratorInfo.isDeclarationOfFunction(), EqualLoc); if (Init.isInvalid()) SkipUntil(tok::comma, true, true); } @@ -2246,8 +2250,7 @@ Parser::MemInitResult Parser::ParseMemInitializer(Decl *ConstructorDecl) { ParseOptionalCXXScopeSpecifier(SS, ParsedType(), false); ParsedType TemplateTypeTy; if (Tok.is(tok::annot_template_id)) { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name) { AnnotateTemplateIdTokenAsType(); diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp index 4e94ed9323b8..fc64ae022654 100644 --- a/lib/Parse/ParseExpr.cpp +++ b/lib/Parse/ParseExpr.cpp @@ -220,7 +220,7 @@ ExprResult Parser::ParseAssignmentExpression() { if (Tok.is(tok::kw_throw)) return ParseThrowExpression(); - ExprResult LHS = ParseCastExpression(false, false, ParsedType()); + ExprResult LHS = ParseCastExpression(/*isUnaryExpression=*/false); return ParseRHSOfBinaryExpression(move(LHS), prec::Assignment); } @@ -304,13 +304,14 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { ColonLoc = ConsumeToken(); } else { // Otherwise, we're missing a ':'. Assume that this was a typo that the - // user forgot. If we're not in a macro instantion, we can suggest a + // user forgot. If we're not in a macro instantiation, we can suggest a // fixit hint. If there were two spaces before the current token, // suggest inserting the colon in between them, otherwise insert ": ". SourceLocation FILoc = Tok.getLocation(); const char *FIText = ": "; - if (FILoc.isFileID()) { - const SourceManager &SM = PP.getSourceManager(); + const SourceManager &SM = PP.getSourceManager(); + if (FILoc.isFileID() || PP.isAtStartOfMacroExpansion(FILoc)) { + FILoc = SM.getInstantiationLoc(FILoc); bool IsInvalid = false; const char *SourcePtr = SM.getCharacterData(FILoc.getFileLocWithOffset(-1), &IsInvalid); @@ -414,12 +415,12 @@ Parser::ParseRHSOfBinaryExpression(ExprResult LHS, prec::Level MinPrec) { /// ExprResult Parser::ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, - ParsedType TypeOfCast) { + bool isTypeCast) { bool NotCastExpr; ExprResult Res = ParseCastExpression(isUnaryExpression, isAddressOfOperand, NotCastExpr, - TypeOfCast); + isTypeCast); if (NotCastExpr) Diag(Tok, diag::err_expected_expression); return move(Res); @@ -588,7 +589,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, ExprResult Parser::ParseCastExpression(bool isUnaryExpression, bool isAddressOfOperand, bool &NotCastExpr, - ParsedType TypeOfCast) { + bool isTypeCast) { ExprResult Res; tok::TokenKind SavedKind = Tok.getKind(); NotCastExpr = false; @@ -619,7 +620,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, ColonProtectionRAIIObject X(*this, false); Res = ParseParenExpression(ParenExprType, false/*stopIfCastExr*/, - TypeOfCast, CastTy, RParenLoc); + isTypeCast, CastTy, RParenLoc); } switch (ParenExprType) { @@ -791,7 +792,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, return ParseBuiltinPrimaryExpression(); case tok::kw___null: return Actions.ActOnGNUNullExpr(ConsumeToken()); - break; + case tok::plusplus: // unary-expression: '++' unary-expression [C99] case tok::minusminus: { // unary-expression: '--' unary-expression [C99] // C++ [expr.unary] has: @@ -951,12 +952,11 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, return ExprError(); if (!Tok.is(tok::annot_cxxscope)) return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, TypeOfCast); + NotCastExpr, isTypeCast); Token Next = NextToken(); if (Next.is(tok::annot_template_id)) { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Next.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Next); if (TemplateId->Kind == TNK_Type_template) { // We have a qualified template-id that we know refers to a // type, translate it into a type and continue parsing as a @@ -965,7 +965,7 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, ParseOptionalCXXScopeSpecifier(SS, ParsedType(), false); AnnotateTemplateIdTokenAsType(); return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, TypeOfCast); + NotCastExpr, isTypeCast); } } @@ -975,15 +975,14 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, } case tok::annot_template_id: { // [C++] template-id - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template) { // We have a template-id that we know refers to a type, // translate it into a type and continue parsing as a cast // expression. AnnotateTemplateIdTokenAsType(); return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, TypeOfCast); + NotCastExpr, isTypeCast); } // Fall through to treat the template-id as an id-expression. @@ -1101,17 +1100,20 @@ ExprResult Parser::ParseCastExpression(bool isUnaryExpression, return ParseObjCAtExpression(AtLoc); } case tok::caret: - return ParsePostfixExpressionSuffix(ParseBlockLiteralExpression()); - case tok::code_completion: + Res = ParseBlockLiteralExpression(); + break; + case tok::code_completion: { Actions.CodeCompleteOrdinaryName(getCurScope(), Sema::PCC_Expression); ConsumeCodeCompletionToken(); return ParseCastExpression(isUnaryExpression, isAddressOfOperand, - NotCastExpr, TypeOfCast); + NotCastExpr, isTypeCast); + } case tok::l_square: - // These can be followed by postfix-expr pieces. - if (getLang().ObjC1) - return ParsePostfixExpressionSuffix(ParseObjCMessageExpression()); - // FALL THROUGH. + if (getLang().ObjC1) { + Res = ParseObjCMessageExpression(); + break; + } + // FALL THROUGH. default: NotCastExpr = true; return ExprError(); @@ -1261,7 +1263,6 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) { if (Tok.isNot(tok::r_paren)) { if (ParseExpressionList(ArgExprs, CommaLocs, &Sema::CodeCompleteCall, LHS.get())) { - SkipUntil(tok::r_paren); LHS = ExprError(); } } @@ -1424,7 +1425,7 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok, EnterExpressionEvaluationContext Unevaluated(Actions, Sema::Unevaluated); Operand = ParseParenExpression(ExprType, true/*stopIfCastExpr*/, - ParsedType(), CastTy, RParenLoc); + false, CastTy, RParenLoc); CastRange = SourceRange(LParenLoc, RParenLoc); // If ParseParenExpression parsed a '(typename)' sequence only, then this is @@ -1717,7 +1718,7 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { ConsumeParen()); break; } -} + } if (Res.isInvalid()) return ExprError(); @@ -1740,10 +1741,15 @@ ExprResult Parser::ParseBuiltinPrimaryExpression() { /// '(' type-name ')' '{' initializer-list ',' '}' /// cast-expression: [C99 6.5.4] /// '(' type-name ')' cast-expression -/// +/// [ARC] bridged-cast-expression +/// +/// [ARC] bridged-cast-expression: +/// (__bridge type-name) cast-expression +/// (__bridge_transfer type-name) cast-expression +/// (__bridge_retained type-name) cast-expression ExprResult Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, - ParsedType TypeOfCast, ParsedType &CastTy, + bool isTypeCast, ParsedType &CastTy, SourceLocation &RParenLoc) { assert(Tok.is(tok::l_paren) && "Not a paren expr!"); GreaterThanIsOperatorScope G(GreaterThanIsOperator, true); @@ -1772,7 +1778,43 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, // If the substmt parsed correctly, build the AST node. if (!Stmt.isInvalid()) Result = Actions.ActOnStmtExpr(OpenLoc, Stmt.take(), Tok.getLocation()); - + } else if (ExprType >= CompoundLiteral && + (Tok.is(tok::kw___bridge) || + Tok.is(tok::kw___bridge_transfer) || + Tok.is(tok::kw___bridge_retained) || + Tok.is(tok::kw___bridge_retain))) { + tok::TokenKind tokenKind = Tok.getKind(); + SourceLocation BridgeKeywordLoc = ConsumeToken(); + + // Parse an Objective-C ARC ownership cast expression. + ObjCBridgeCastKind Kind; + if (tokenKind == tok::kw___bridge) + Kind = OBC_Bridge; + else if (tokenKind == tok::kw___bridge_transfer) + Kind = OBC_BridgeTransfer; + else if (tokenKind == tok::kw___bridge_retained) + Kind = OBC_BridgeRetained; + else { + // As a hopefully temporary workaround, allow __bridge_retain as + // a synonym for __bridge_retained, but only in system headers. + assert(tokenKind == tok::kw___bridge_retain); + Kind = OBC_BridgeRetained; + if (!PP.getSourceManager().isInSystemHeader(BridgeKeywordLoc)) + Diag(BridgeKeywordLoc, diag::err_arc_bridge_retain) + << FixItHint::CreateReplacement(BridgeKeywordLoc, + "__bridge_retained"); + } + + TypeResult Ty = ParseTypeName(); + SourceLocation RParenLoc = MatchRHSPunctuation(tok::r_paren, OpenLoc); + ExprResult SubExpr = ParseCastExpression(/*isUnaryExpression=*/false); + + if (Ty.isInvalid() || SubExpr.isInvalid()) + return ExprError(); + + return Actions.ActOnObjCBridgedCast(getCurScope(), OpenLoc, Kind, + BridgeKeywordLoc, Ty.get(), + RParenLoc, SubExpr.get()); } else if (ExprType >= CompoundLiteral && isTypeIdInParens(isAmbiguousTypeId)) { @@ -1787,20 +1829,23 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, return ParseCXXAmbiguousParenExpression(ExprType, CastTy, OpenLoc, RParenLoc); - TypeResult Ty; - - { - InMessageExpressionRAIIObject InMessage(*this, false); - Ty = ParseTypeName(); - } + // Parse the type declarator. + DeclSpec DS(AttrFactory); + ParseSpecifierQualifierList(DS); + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + ParseDeclarator(DeclaratorInfo); // If our type is followed by an identifier and either ':' or ']', then // this is probably an Objective-C message send where the leading '[' is // missing. Recover as if that were the case. - if (!Ty.isInvalid() && Tok.is(tok::identifier) && !InMessageExpression && - getLang().ObjC1 && !Ty.get().get().isNull() && - (NextToken().is(tok::colon) || NextToken().is(tok::r_square)) && - Ty.get().get()->isObjCObjectOrInterfaceType()) { + if (!DeclaratorInfo.isInvalidType() && Tok.is(tok::identifier) && + !InMessageExpression && getLang().ObjC1 && + (NextToken().is(tok::colon) || NextToken().is(tok::r_square))) { + TypeResult Ty; + { + InMessageExpressionRAIIObject InMessage(*this, false); + Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); + } Result = ParseObjCMessageExpressionBody(SourceLocation(), SourceLocation(), Ty.get(), 0); @@ -1813,21 +1858,31 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, if (Tok.is(tok::l_brace)) { ExprType = CompoundLiteral; + TypeResult Ty; + { + InMessageExpressionRAIIObject InMessage(*this, false); + Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); + } return ParseCompoundLiteralExpression(Ty.get(), OpenLoc, RParenLoc); } if (ExprType == CastExpr) { // We parsed '(' type-name ')' and the thing after it wasn't a '{'. - if (Ty.isInvalid()) + if (DeclaratorInfo.isInvalidType()) return ExprError(); - CastTy = Ty.get(); - // Note that this doesn't parse the subsequent cast-expression, it just // returns the parsed type to the callee. - if (stopIfCastExpr) + if (stopIfCastExpr) { + TypeResult Ty; + { + InMessageExpressionRAIIObject InMessage(*this, false); + Ty = Actions.ActOnTypeName(getCurScope(), DeclaratorInfo); + } + CastTy = Ty.get(); return ExprResult(); + } // Reject the cast of super idiom in ObjC. if (Tok.is(tok::identifier) && getLang().ObjC1 && @@ -1841,17 +1896,21 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, // Parse the cast-expression that follows it next. // TODO: For cast expression with CastTy. - Result = ParseCastExpression(false, false, CastTy); - if (!Result.isInvalid()) - Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc, CastTy, + Result = ParseCastExpression(/*isUnaryExpression=*/false, + /*isAddressOfOperand=*/false, + /*isTypeCast=*/true); + if (!Result.isInvalid()) { + Result = Actions.ActOnCastExpr(getCurScope(), OpenLoc, + DeclaratorInfo, CastTy, RParenLoc, Result.take()); + } return move(Result); } Diag(Tok, diag::err_expected_lbrace_in_compound_literal); return ExprError(); } - } else if (TypeOfCast) { + } else if (isTypeCast) { // Parse the expression-list. InMessageExpressionRAIIObject InMessage(*this, false); @@ -1861,7 +1920,7 @@ Parser::ParseParenExpression(ParenParseOption &ExprType, bool stopIfCastExpr, if (!ParseExpressionList(ArgExprs, CommaLocs)) { ExprType = SimpleExpr; Result = Actions.ActOnParenOrParenListExpr(OpenLoc, Tok.getLocation(), - move_arg(ArgExprs), TypeOfCast); + move_arg(ArgExprs)); } } else { InMessageExpressionRAIIObject InMessage(*this, false); @@ -2174,6 +2233,7 @@ ExprResult Parser::ParseBlockLiteralExpression() { SourceLocation(), 0, 0, 0, true, SourceLocation(), + SourceLocation(), EST_None, SourceLocation(), 0, 0, 0, 0, diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp index eab7114284de..b32eeda24279 100644 --- a/lib/Parse/ParseExprCXX.cpp +++ b/lib/Parse/ParseExprCXX.cpp @@ -245,8 +245,7 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, // So we need to check whether the simple-template-id is of the // right kind (it should name a type or be dependent), and then // convert it into a type within the nested-name-specifier. - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (CheckForDestructor && GetLookAheadToken(2).is(tok::tilde)) { *MayBePseudoDestructor = true; return false; @@ -281,10 +280,6 @@ bool Parser::ParseOptionalCXXScopeSpecifier(CXXScopeSpec &SS, SS.SetInvalid(SourceRange(StartLoc, CCLoc)); } - // If we are caching tokens we will process the TemplateId again, - // otherwise destroy it. - if (!PP.isBacktrackEnabled()) - TemplateId->Destroy(); continue; } @@ -543,7 +538,14 @@ ExprResult Parser::ParseCXXCasts() { if (ExpectAndConsume(tok::less, diag::err_expected_less_after, CastName)) return ExprError(); - TypeResult CastTy = ParseTypeName(); + // Parse the common declaration-specifiers piece. + DeclSpec DS(AttrFactory); + ParseSpecifierQualifierList(DS); + + // Parse the abstract-declarator, if present. + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + ParseDeclarator(DeclaratorInfo); + SourceLocation RAngleBracketLoc = Tok.getLocation(); if (ExpectAndConsume(tok::greater, diag::err_expected_greater)) @@ -559,9 +561,9 @@ ExprResult Parser::ParseCXXCasts() { // Match the ')'. RParenLoc = MatchRHSPunctuation(tok::r_paren, LParenLoc); - if (!Result.isInvalid() && !CastTy.isInvalid()) + if (!Result.isInvalid() && !DeclaratorInfo.isInvalidType()) Result = Actions.ActOnCXXNamedCast(OpLoc, Kind, - LAngleBracketLoc, CastTy.get(), + LAngleBracketLoc, DeclaratorInfo, RAngleBracketLoc, LParenLoc, Result.take(), RParenLoc); @@ -785,12 +787,12 @@ ExprResult Parser::ParseThrowExpression() { case tok::r_brace: case tok::colon: case tok::comma: - return Actions.ActOnCXXThrow(ThrowLoc, 0); + return Actions.ActOnCXXThrow(getCurScope(), ThrowLoc, 0); default: ExprResult Expr(ParseAssignmentExpression()); if (Expr.isInvalid()) return move(Expr); - return Actions.ActOnCXXThrow(ThrowLoc, Expr.take()); + return Actions.ActOnCXXThrow(getCurScope(), ThrowLoc, Expr.take()); } } @@ -1606,8 +1608,7 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, // unqualified-id: // template-id (already parsed and annotated) if (Tok.is(tok::annot_template_id)) { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation*>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); // If the template-name names the current class, then this is a constructor if (AllowConstructorName && TemplateId->Name && @@ -1630,7 +1631,6 @@ bool Parser::ParseUnqualifiedId(CXXScopeSpec &SS, bool EnteringContext, /*NontrivialTypeSourceInfo=*/true), TemplateId->TemplateNameLoc, TemplateId->RAngleLoc); - TemplateId->Destroy(); ConsumeToken(); return false; } @@ -1755,7 +1755,7 @@ Parser::ParseCXXNewExpression(bool UseGlobal, SourceLocation Start) { SourceRange TypeIdParens; DeclSpec DS(AttrFactory); - Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + Declarator DeclaratorInfo(DS, Declarator::CXXNewContext); if (Tok.is(tok::l_paren)) { // If it turns out to be a placement, we change the type location. PlacementLParen = ConsumeParen(); @@ -2200,7 +2200,8 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, Result = ParseCastExpression(false/*isUnaryExpression*/, false/*isAddressofOperand*/, NotCastExpr, - ParsedType()/*TypeOfCast*/); + // type-id has priority. + true/*isTypeCast*/); } // If we parsed a cast-expression, it's really a type-id, otherwise it's @@ -2219,7 +2220,11 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, ConsumeAnyToken(); if (ParseAs >= CompoundLiteral) { - TypeResult Ty = ParseTypeName(); + // Parse the type declarator. + DeclSpec DS(AttrFactory); + ParseSpecifierQualifierList(DS); + Declarator DeclaratorInfo(DS, Declarator::TypeNameContext); + ParseDeclarator(DeclaratorInfo); // Match the ')'. if (Tok.is(tok::r_paren)) @@ -2229,21 +2234,21 @@ Parser::ParseCXXAmbiguousParenExpression(ParenParseOption &ExprType, if (ParseAs == CompoundLiteral) { ExprType = CompoundLiteral; + TypeResult Ty = ParseTypeName(); return ParseCompoundLiteralExpression(Ty.get(), LParenLoc, RParenLoc); } // We parsed '(' type-id ')' and the thing after it wasn't a '{'. assert(ParseAs == CastExpr); - if (Ty.isInvalid()) + if (DeclaratorInfo.isInvalidType()) return ExprError(); - CastTy = Ty.get(); - // Result is what ParseCastExpression returned earlier. if (!Result.isInvalid()) - Result = Actions.ActOnCastExpr(getCurScope(), LParenLoc, CastTy, RParenLoc, - Result.take()); + Result = Actions.ActOnCastExpr(getCurScope(), LParenLoc, + DeclaratorInfo, CastTy, + RParenLoc, Result.take()); return move(Result); } diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index a8c18c01a291..7641565d1cc3 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -480,6 +480,10 @@ void Parser::ParseObjCInterfaceDeclList(Decl *interfaceDecl, /// retain /// copy /// nonatomic +/// atomic +/// strong +/// weak +/// unsafe_unretained /// void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS, Decl *ClassDecl) { assert(Tok.getKind() == tok::l_paren); @@ -504,16 +508,22 @@ void Parser::ParseObjCPropertyAttribute(ObjCDeclSpec &DS, Decl *ClassDecl) { DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readonly); else if (II->isStr("assign")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_assign); + else if (II->isStr("unsafe_unretained")) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_unsafe_unretained); else if (II->isStr("readwrite")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_readwrite); else if (II->isStr("retain")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_retain); + else if (II->isStr("strong")) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_strong); else if (II->isStr("copy")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_copy); else if (II->isStr("nonatomic")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_nonatomic); else if (II->isStr("atomic")) DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_atomic); + else if (II->isStr("weak")) + DS.setPropertyAttributes(ObjCDeclSpec::DQ_PR_weak); else if (II->isStr("getter") || II->isStr("setter")) { bool IsSetter = II->getNameStart()[0] == 's'; @@ -775,11 +785,12 @@ ParsedType Parser::ParseObjCTypeName(ObjCDeclSpec &DS, ParsedType Ty; if (isTypeSpecifierQualifier()) { - TypeResult TypeSpec = ParseTypeName(0, Declarator::ObjCPrototypeContext); + TypeResult TypeSpec = + ParseTypeName(0, Declarator::ObjCPrototypeContext, &DS); if (!TypeSpec.isInvalid()) Ty = TypeSpec.get(); } - + if (Tok.is(tok::r_paren)) ConsumeParen(); else if (Tok.getLocation() == TypeStartLoc) { @@ -1622,10 +1633,7 @@ StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { if (Tok.isNot(tok::ellipsis)) { DeclSpec DS(AttrFactory); ParseDeclarationSpecifiers(DS); - // For some odd reason, the name of the exception variable is - // optional. As a result, we need to use "PrototypeContext", because - // we must accept either 'declarator' or 'abstract-declarator' here. - Declarator ParmDecl(DS, Declarator::PrototypeContext); + Declarator ParmDecl(DS, Declarator::ObjCCatchContext); ParseDeclarator(ParmDecl); // Inform the actions module about the declarator, so it @@ -1690,6 +1698,29 @@ StmtResult Parser::ParseObjCTryStmt(SourceLocation atLoc) { FinallyStmt.take()); } +/// objc-autoreleasepool-statement: +/// @autoreleasepool compound-statement +/// +StmtResult +Parser::ParseObjCAutoreleasePoolStmt(SourceLocation atLoc) { + ConsumeToken(); // consume autoreleasepool + if (Tok.isNot(tok::l_brace)) { + Diag(Tok, diag::err_expected_lbrace); + return StmtError(); + } + // Enter a scope to hold everything within the compound stmt. Compound + // statements can always hold declarations. + ParseScope BodyScope(this, Scope::DeclScope); + + StmtResult AutoreleasePoolBody(ParseCompoundStatementBody()); + + BodyScope.Exit(); + if (AutoreleasePoolBody.isInvalid()) + AutoreleasePoolBody = Actions.ActOnNullStmt(Tok.getLocation()); + return Actions.ActOnObjCAutoreleasePoolStmt(atLoc, + AutoreleasePoolBody.take()); +} + /// objc-method-def: objc-method-proto ';'[opt] '{' body '}' /// Decl *Parser::ParseObjCMethodDefinition() { @@ -1765,6 +1796,9 @@ StmtResult Parser::ParseObjCAtStatement(SourceLocation AtLoc) { if (Tok.isObjCAtKeyword(tok::objc_synchronized)) return ParseObjCSynchronizedStmt(AtLoc); + + if (Tok.isObjCAtKeyword(tok::objc_autoreleasepool)) + return ParseObjCAutoreleasePoolStmt(AtLoc); ExprResult Res(ParseExpressionWithLeadingAt(AtLoc)); if (Res.isInvalid()) { diff --git a/lib/Parse/ParseStmt.cpp b/lib/Parse/ParseStmt.cpp index 6cc8b57b61fe..b91bca55a9dc 100644 --- a/lib/Parse/ParseStmt.cpp +++ b/lib/Parse/ParseStmt.cpp @@ -226,7 +226,7 @@ Retry: case tok::semi: { // C99 6.8.3p3: expression[opt] ';' SourceLocation LeadingEmptyMacroLoc; if (Tok.hasLeadingEmptyMacro()) - LeadingEmptyMacroLoc = PP.getLastEmptyMacroInstantiationLoc(); + LeadingEmptyMacroLoc = PP.getLastEmptyMacroExpansionLoc(); return Actions.ActOnNullStmt(ConsumeToken(), LeadingEmptyMacroLoc); } @@ -502,6 +502,7 @@ StmtResult Parser::ParseCaseStatement(ParsedAttributes &attrs, bool MissingCase, StmtTy *DeepestParsedCaseStmt = 0; // While we have case statements, eat and stack them. + SourceLocation ColonLoc; do { SourceLocation CaseLoc = MissingCase ? Expr.get()->getExprLoc() : ConsumeToken(); // eat the 'case'. @@ -539,7 +540,6 @@ StmtResult Parser::ParseCaseStatement(ParsedAttributes &attrs, bool MissingCase, ColonProtection.restore(); - SourceLocation ColonLoc; if (Tok.is(tok::colon)) { ColonLoc = ConsumeToken(); @@ -589,8 +589,8 @@ StmtResult Parser::ParseCaseStatement(ParsedAttributes &attrs, bool MissingCase, } else { // Nicely diagnose the common error "switch (X) { case 4: }", which is // not valid. - // FIXME: add insertion hint. - Diag(Tok, diag::err_label_end_of_compound_statement); + SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc); + Diag(AfterColonLoc, diag::err_label_end_of_compound_statement); SubStmt = true; } @@ -634,7 +634,8 @@ StmtResult Parser::ParseDefaultStatement(ParsedAttributes &attrs) { // Diagnose the common error "switch (X) {... default: }", which is not valid. if (Tok.is(tok::r_brace)) { - Diag(Tok, diag::err_label_end_of_compound_statement); + SourceLocation AfterColonLoc = PP.getLocForEndOfToken(ColonLoc); + Diag(AfterColonLoc, diag::err_label_end_of_compound_statement); return StmtError(); } @@ -646,6 +647,10 @@ StmtResult Parser::ParseDefaultStatement(ParsedAttributes &attrs) { SubStmt.get(), getCurScope()); } +StmtResult Parser::ParseCompoundStatement(ParsedAttributes &Attr, + bool isStmtExpr) { + return ParseCompoundStatement(Attr, isStmtExpr, Scope::DeclScope); +} /// ParseCompoundStatement - Parse a "{}" block. /// @@ -675,14 +680,15 @@ StmtResult Parser::ParseDefaultStatement(ParsedAttributes &attrs) { /// [OMP] flush-directive /// StmtResult Parser::ParseCompoundStatement(ParsedAttributes &attrs, - bool isStmtExpr) { + bool isStmtExpr, + unsigned ScopeFlags) { //FIXME: Use attributes? assert(Tok.is(tok::l_brace) && "Not a compount stmt!"); // Enter a scope to hold everything within the compound stmt. Compound // statements can always hold declarations. - ParseScope CompoundScope(this, Scope::DeclScope); + ParseScope CompoundScope(this, ScopeFlags); // Parse the statements in the body. return ParseCompoundStatementBody(isStmtExpr); @@ -1909,7 +1915,8 @@ StmtResult Parser::ParseCXXTryBlockCommon(SourceLocation TryLoc) { return StmtError(Diag(Tok, diag::err_expected_lbrace)); // FIXME: Possible draft standard bug: attribute-specifier should be allowed? ParsedAttributesWithRange attrs(AttrFactory); - StmtResult TryBlock(ParseCompoundStatement(attrs)); + StmtResult TryBlock(ParseCompoundStatement(attrs, /*isStmtExpr=*/false, + Scope::DeclScope|Scope::TryScope)); if (TryBlock.isInvalid()) return move(TryBlock); diff --git a/lib/Parse/ParseTemplate.cpp b/lib/Parse/ParseTemplate.cpp index aa89d75c6d1b..9eab40a3ecdf 100644 --- a/lib/Parse/ParseTemplate.cpp +++ b/lib/Parse/ParseTemplate.cpp @@ -861,8 +861,7 @@ bool Parser::AnnotateTemplateIdToken(TemplateTy Template, TemplateNameKind TNK, void Parser::AnnotateTemplateIdTokenAsType() { assert(Tok.is(tok::annot_template_id) && "Requires template-id tokens"); - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); assert((TemplateId->Kind == TNK_Type_template || TemplateId->Kind == TNK_Dependent_template_name) && "Only works for type and dependent templates"); @@ -888,7 +887,6 @@ void Parser::AnnotateTemplateIdTokenAsType() { // Replace the template-id annotation token, and possible the scope-specifier // that precedes it, with the typename annotation token. PP.AnnotateCachedTokens(Tok); - TemplateId->Destroy(); } /// \brief Determine whether the given token can end a template argument. diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp index 78d2c9091b1b..2ba0fc673f67 100644 --- a/lib/Parse/ParseTentative.cpp +++ b/lib/Parse/ParseTentative.cpp @@ -908,8 +908,7 @@ Parser::TPResult Parser::isCXXDeclarationSpecifier() { return TPResult::True(); case tok::annot_template_id: { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind != TNK_Type_template) return TPResult::False(); CXXScopeSpec SS; diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index f19472ccdc04..5c502907bc15 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -124,9 +124,8 @@ void Parser::SuggestParentheses(SourceLocation Loc, unsigned DK, /// MatchRHSPunctuation - For punctuation with a LHS and RHS (e.g. '['/']'), /// this helper function matches and consumes the specified RHS token if -/// present. If not present, it emits the specified diagnostic indicating -/// that the parser failed to match the RHS of the token at LHSLoc. LHSName -/// should be the name of the unmatched LHS token. +/// present. If not present, it emits a corresponding diagnostic indicating +/// that the parser failed to match the RHS of the token at LHSLoc. SourceLocation Parser::MatchRHSPunctuation(tok::TokenKind RHSTok, SourceLocation LHSLoc) { @@ -486,6 +485,7 @@ void Parser::Initialize() { /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the /// action tells us to. This returns true if the EOF was encountered. bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { + DelayedCleanupPoint CleanupRAII(TopLevelDeclCleanupPool); while (Tok.is(tok::annot_pragma_unused)) HandlePragmaUnused(); @@ -548,6 +548,7 @@ void Parser::ParseTranslationUnit() { Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributesWithRange &attrs, ParsingDeclSpec *DS) { + DelayedCleanupPoint CleanupRAII(TopLevelDeclCleanupPool); ParenBraceBracketBalancer BalancerRAIIObj(*this); Decl *SingleDecl = 0; @@ -1155,6 +1156,18 @@ Parser::ExprResult Parser::ParseSimpleAsm(SourceLocation *EndLoc) { return move(Result); } +/// \brief Get the TemplateIdAnnotation from the token and put it in the +/// cleanup pool so that it gets destroyed when parsing the current top level +/// declaration is finished. +TemplateIdAnnotation *Parser::takeTemplateIdAnnotation(const Token &tok) { + assert(tok.is(tok::annot_template_id) && "Expected template-id token"); + TemplateIdAnnotation * + Id = static_cast<TemplateIdAnnotation *>(tok.getAnnotationValue()); + TopLevelDeclCleanupPool.delayMemberFunc< TemplateIdAnnotation, + &TemplateIdAnnotation::Destroy>(Id); + return Id; +} + /// TryAnnotateTypeOrScopeToken - If the current token position is on a /// typename (possibly qualified in C++) or a C++ scope specifier not followed /// by a typename, TryAnnotateTypeOrScopeToken will replace one or more tokens @@ -1209,8 +1222,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { *Tok.getIdentifierInfo(), Tok.getLocation()); } else if (Tok.is(tok::annot_template_id)) { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Function_template) { Diag(Tok, diag::err_typename_refers_to_non_type_template) << Tok.getAnnotationRange(); @@ -1228,7 +1240,6 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { TemplateId->LAngleLoc, TemplateArgsPtr, TemplateId->RAngleLoc); - TemplateId->Destroy(); } else { Diag(Tok, diag::err_expected_type_name_after_typename) << SS.getRange(); @@ -1311,8 +1322,7 @@ bool Parser::TryAnnotateTypeOrScopeToken(bool EnteringContext) { } if (Tok.is(tok::annot_template_id)) { - TemplateIdAnnotation *TemplateId - = static_cast<TemplateIdAnnotation *>(Tok.getAnnotationValue()); + TemplateIdAnnotation *TemplateId = takeTemplateIdAnnotation(Tok); if (TemplateId->Kind == TNK_Type_template) { // A template-id that refers to a type was parsed into a // template-id annotation in a context where we weren't allowed diff --git a/lib/Rewrite/FixItRewriter.cpp b/lib/Rewrite/FixItRewriter.cpp index 8dcc5dcba789..e50793e2775f 100644 --- a/lib/Rewrite/FixItRewriter.cpp +++ b/lib/Rewrite/FixItRewriter.cpp @@ -121,8 +121,7 @@ void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel, if (Hint.CodeToInsert.empty()) { // We're removing code. - if (Rewrite.RemoveText(Hint.RemoveRange.getBegin(), - Rewrite.getRangeSize(Hint.RemoveRange))) + if (Rewrite.RemoveText(Hint.RemoveRange)) Failed = true; continue; } diff --git a/lib/Rewrite/HTMLRewrite.cpp b/lib/Rewrite/HTMLRewrite.cpp index df08cd7cbf3d..0b54755dc58c 100644 --- a/lib/Rewrite/HTMLRewrite.cpp +++ b/lib/Rewrite/HTMLRewrite.cpp @@ -516,7 +516,7 @@ void html::HighlightMacros(Rewriter &R, FileID FID, const Preprocessor& PP) { } // Okay, we have the first token of a macro expansion: highlight the - // instantiation by inserting a start tag before the macro instantiation and + // expansion by inserting a start tag before the macro expansion and // end tag after it. std::pair<SourceLocation, SourceLocation> LLoc = SM.getInstantiationRange(Tok.getLocation()); diff --git a/lib/Rewrite/RewriteObjC.cpp b/lib/Rewrite/RewriteObjC.cpp index 8cdb55a0c1c6..8202164ebce5 100644 --- a/lib/Rewrite/RewriteObjC.cpp +++ b/lib/Rewrite/RewriteObjC.cpp @@ -2110,8 +2110,7 @@ Stmt *RewriteObjC::RewriteAtEncode(ObjCEncodeExpr *Exp) { QualType StrType = Context->getPointerType(Context->CharTy); std::string StrEncoding; Context->getObjCEncodingForType(Exp->getEncodedType(), StrEncoding); - Expr *Replacement = StringLiteral::Create(*Context,StrEncoding.c_str(), - StrEncoding.length(), + Expr *Replacement = StringLiteral::Create(*Context, StrEncoding, false, false, StrType, SourceLocation()); ReplaceStmt(Exp, Replacement); @@ -2129,9 +2128,8 @@ Stmt *RewriteObjC::RewriteAtSelector(ObjCSelectorExpr *Exp) { llvm::SmallVector<Expr*, 8> SelExprs; QualType argType = Context->getPointerType(Context->CharTy); SelExprs.push_back(StringLiteral::Create(*Context, - Exp->getSelector().getAsString().c_str(), - Exp->getSelector().getAsString().size(), - false, false, argType, + Exp->getSelector().getAsString(), + false, false, argType, SourceLocation())); CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, &SelExprs[0], SelExprs.size()); @@ -2798,8 +2796,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, llvm::SmallVector<Expr*, 8> ClsExprs; QualType argType = Context->getPointerType(Context->CharTy); ClsExprs.push_back(StringLiteral::Create(*Context, - ClassDecl->getIdentifier()->getNameStart(), - ClassDecl->getIdentifier()->getLength(), + ClassDecl->getIdentifier()->getName(), false, false, argType, SourceLocation())); CallExpr *Cls = SynthesizeCallToFunctionDecl(GetMetaClassFunctionDecl, &ClsExprs[0], @@ -2877,8 +2874,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, = Exp->getClassReceiver()->getAs<ObjCObjectType>()->getInterface(); IdentifierInfo *clsName = Class->getIdentifier(); ClsExprs.push_back(StringLiteral::Create(*Context, - clsName->getNameStart(), - clsName->getLength(), + clsName->getName(), false, false, argType, SourceLocation())); CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, @@ -2909,8 +2905,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, llvm::SmallVector<Expr*, 8> ClsExprs; QualType argType = Context->getPointerType(Context->CharTy); ClsExprs.push_back(StringLiteral::Create(*Context, - ClassDecl->getIdentifier()->getNameStart(), - ClassDecl->getIdentifier()->getLength(), + ClassDecl->getIdentifier()->getName(), false, false, argType, SourceLocation())); CallExpr *Cls = SynthesizeCallToFunctionDecl(GetClassFunctionDecl, &ClsExprs[0], @@ -2991,8 +2986,7 @@ Stmt *RewriteObjC::SynthMessageExpr(ObjCMessageExpr *Exp, llvm::SmallVector<Expr*, 8> SelExprs; QualType argType = Context->getPointerType(Context->CharTy); SelExprs.push_back(StringLiteral::Create(*Context, - Exp->getSelector().getAsString().c_str(), - Exp->getSelector().getAsString().size(), + Exp->getSelector().getAsString(), false, false, argType, SourceLocation())); CallExpr *SelExp = SynthesizeCallToFunctionDecl(SelGetUidFunctionDecl, &SelExprs[0], SelExprs.size(), diff --git a/lib/Rewrite/Rewriter.cpp b/lib/Rewrite/Rewriter.cpp index 51fe379fee0d..92f516074ec9 100644 --- a/lib/Rewrite/Rewriter.cpp +++ b/lib/Rewrite/Rewriter.cpp @@ -231,10 +231,44 @@ RewriteBuffer &Rewriter::getEditBuffer(FileID FID) { /// InsertText - Insert the specified string at the specified location in the /// original buffer. bool Rewriter::InsertText(SourceLocation Loc, llvm::StringRef Str, - bool InsertAfter) { + bool InsertAfter, bool indentNewLines) { + using llvm::StringRef; + if (!isRewritable(Loc)) return true; FileID FID; unsigned StartOffs = getLocationOffsetAndFileID(Loc, FID); + + llvm::SmallString<128> indentedStr; + if (indentNewLines && Str.find('\n') != StringRef::npos) { + StringRef MB = SourceMgr->getBufferData(FID); + + unsigned lineNo = SourceMgr->getLineNumber(FID, StartOffs) - 1; + const SrcMgr::ContentCache * + Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); + unsigned lineOffs = Content->SourceLineCache[lineNo]; + + // Find the whitespace at the start of the line. + StringRef indentSpace; + { + unsigned i = lineOffs; + while (isWhitespace(MB[i])) + ++i; + indentSpace = MB.substr(lineOffs, i-lineOffs); + } + + llvm::SmallVector<StringRef, 4> lines; + Str.split(lines, "\n"); + + for (unsigned i = 0, e = lines.size(); i != e; ++i) { + indentedStr += lines[i]; + if (i < e-1) { + indentedStr += '\n'; + indentedStr += indentSpace; + } + } + Str = indentedStr.str(); + } + getEditBuffer(FID).InsertText(StartOffs, Str, InsertAfter); return false; } @@ -317,6 +351,7 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, SourceLocation parentIndent) { using llvm::StringRef; + if (range.isInvalid()) return true; if (!isRewritable(range.getBegin())) return true; if (!isRewritable(range.getEnd())) return true; if (!isRewritable(parentIndent)) return true; @@ -330,7 +365,7 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, if (StartFileID != EndFileID || StartFileID != parentFileID) return true; - if (StartOff >= EndOff || parentOff >= StartOff) + if (StartOff > EndOff) return true; FileID FID = StartFileID; @@ -343,16 +378,12 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, const SrcMgr::ContentCache * Content = SourceMgr->getSLocEntry(FID).getFile().getContentCache(); - // Find where the line starts for the three offsets. + // Find where the lines start. unsigned parentLineOffs = Content->SourceLineCache[parentLineNo]; unsigned startLineOffs = Content->SourceLineCache[startLineNo]; - unsigned endLineOffs = Content->SourceLineCache[endLineNo]; - - if (startLineOffs == endLineOffs || startLineOffs == parentLineOffs) - return true; // Find the whitespace at the start of each line. - StringRef parentSpace, startSpace, endSpace; + StringRef parentSpace, startSpace; { unsigned i = parentLineOffs; while (isWhitespace(MB[i])) @@ -363,11 +394,6 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, while (isWhitespace(MB[i])) ++i; startSpace = MB.substr(startLineOffs, i-startLineOffs); - - i = endLineOffs; - while (isWhitespace(MB[i])) - ++i; - endSpace = MB.substr(endLineOffs, i-endLineOffs); } if (parentSpace.size() >= startSpace.size()) return true; @@ -378,19 +404,14 @@ bool Rewriter::IncreaseIndentation(CharSourceRange range, // Indent the lines between start/end offsets. RewriteBuffer &RB = getEditBuffer(FID); - for (unsigned i = startLineOffs; i != endLineOffs; ++i) { - if (MB[i] == '\n') { - unsigned startOfLine = i+1; - if (startOfLine == endLineOffs) - break; - StringRef origIndent; - unsigned ws = startOfLine; - while (isWhitespace(MB[ws])) - ++ws; - origIndent = MB.substr(startOfLine, ws-startOfLine); - if (origIndent.startswith(startSpace)) - RB.InsertText(startOfLine, indent, /*InsertAfter=*/false); - } + for (unsigned lineNo = startLineNo; lineNo <= endLineNo; ++lineNo) { + unsigned offs = Content->SourceLineCache[lineNo]; + unsigned i = offs; + while (isWhitespace(MB[i])) + ++i; + StringRef origIndent = MB.substr(offs, i-offs); + if (origIndent.startswith(startSpace)) + RB.InsertText(offs, indent, /*InsertAfter=*/false); } return false; diff --git a/lib/Sema/AnalysisBasedWarnings.cpp b/lib/Sema/AnalysisBasedWarnings.cpp index 9efae6103a4a..7a14855e69cd 100644 --- a/lib/Sema/AnalysisBasedWarnings.cpp +++ b/lib/Sema/AnalysisBasedWarnings.cpp @@ -486,7 +486,8 @@ static void SuggestInitializationFixit(Sema &S, const VarDecl *VD) { const char *initialization = 0; QualType VariableTy = VD->getType().getCanonicalType(); - if (VariableTy->getAs<ObjCObjectPointerType>()) { + if (VariableTy->isObjCObjectPointerType() || + VariableTy->isBlockPointerType()) { // Check if 'nil' is defined. if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("nil"))) initialization = " = nil"; @@ -499,6 +500,13 @@ static void SuggestInitializationFixit(Sema &S, const VarDecl *VD) { initialization = " = false"; else if (VariableTy->isEnumeralType()) return; + else if (VariableTy->isPointerType() || VariableTy->isMemberPointerType()) { + // Check if 'NULL' is defined. + if (S.PP.getMacroInfo(&S.getASTContext().Idents.get("NULL"))) + initialization = " = NULL"; + else + initialization = " = 0"; + } else if (VariableTy->isScalarType()) initialization = " = 0"; @@ -589,7 +597,17 @@ clang::sema::AnalysisBasedWarnings::Policy::Policy() { enableCheckUnreachable = 0; } -clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) : S(s) { +clang::sema::AnalysisBasedWarnings::AnalysisBasedWarnings(Sema &s) + : S(s), + NumFunctionsAnalyzed(0), + NumFunctionsWithBadCFGs(0), + NumCFGBlocks(0), + MaxCFGBlocksPerFunction(0), + NumUninitAnalysisFunctions(0), + NumUninitAnalysisVariables(0), + MaxUninitAnalysisVariablesPerFunction(0), + NumUninitAnalysisBlockVisits(0), + MaxUninitAnalysisBlockVisitsPerFunction(0) { Diagnostic &D = S.getDiagnostics(); DefaultPolicy.enableCheckUnreachable = (unsigned) (D.getDiagnosticLevel(diag::warn_unreachable, SourceLocation()) != @@ -705,8 +723,68 @@ AnalysisBasedWarnings::IssueWarnings(sema::AnalysisBasedWarnings::Policy P, != Diagnostic::Ignored) { if (CFG *cfg = AC.getCFG()) { UninitValsDiagReporter reporter(S); + UninitVariablesAnalysisStats stats; + std::memset(&stats, 0, sizeof(UninitVariablesAnalysisStats)); runUninitializedVariablesAnalysis(*cast<DeclContext>(D), *cfg, AC, - reporter); + reporter, stats); + + if (S.CollectStats && stats.NumVariablesAnalyzed > 0) { + ++NumUninitAnalysisFunctions; + NumUninitAnalysisVariables += stats.NumVariablesAnalyzed; + NumUninitAnalysisBlockVisits += stats.NumBlockVisits; + MaxUninitAnalysisVariablesPerFunction = + std::max(MaxUninitAnalysisVariablesPerFunction, + stats.NumVariablesAnalyzed); + MaxUninitAnalysisBlockVisitsPerFunction = + std::max(MaxUninitAnalysisBlockVisitsPerFunction, + stats.NumBlockVisits); + } } } + + // Collect statistics about the CFG if it was built. + if (S.CollectStats && AC.isCFGBuilt()) { + ++NumFunctionsAnalyzed; + if (CFG *cfg = AC.getCFG()) { + // If we successfully built a CFG for this context, record some more + // detail information about it. + NumCFGBlocks += cfg->getNumBlockIDs(); + MaxCFGBlocksPerFunction = std::max(MaxCFGBlocksPerFunction, + cfg->getNumBlockIDs()); + } else { + ++NumFunctionsWithBadCFGs; + } + } +} + +void clang::sema::AnalysisBasedWarnings::PrintStats() const { + llvm::errs() << "\n*** Analysis Based Warnings Stats:\n"; + + unsigned NumCFGsBuilt = NumFunctionsAnalyzed - NumFunctionsWithBadCFGs; + unsigned AvgCFGBlocksPerFunction = + !NumCFGsBuilt ? 0 : NumCFGBlocks/NumCFGsBuilt; + llvm::errs() << NumFunctionsAnalyzed << " functions analyzed (" + << NumFunctionsWithBadCFGs << " w/o CFGs).\n" + << " " << NumCFGBlocks << " CFG blocks built.\n" + << " " << AvgCFGBlocksPerFunction + << " average CFG blocks per function.\n" + << " " << MaxCFGBlocksPerFunction + << " max CFG blocks per function.\n"; + + unsigned AvgUninitVariablesPerFunction = !NumUninitAnalysisFunctions ? 0 + : NumUninitAnalysisVariables/NumUninitAnalysisFunctions; + unsigned AvgUninitBlockVisitsPerFunction = !NumUninitAnalysisFunctions ? 0 + : NumUninitAnalysisBlockVisits/NumUninitAnalysisFunctions; + llvm::errs() << NumUninitAnalysisFunctions + << " functions analyzed for uninitialiazed variables\n" + << " " << NumUninitAnalysisVariables << " variables analyzed.\n" + << " " << AvgUninitVariablesPerFunction + << " average variables per function.\n" + << " " << MaxUninitAnalysisVariablesPerFunction + << " max variables per function.\n" + << " " << NumUninitAnalysisBlockVisits << " block visits.\n" + << " " << AvgUninitBlockVisitsPerFunction + << " average block visits per function.\n" + << " " << MaxUninitAnalysisBlockVisitsPerFunction + << " max block visits per function.\n"; } diff --git a/lib/Sema/AttributeList.cpp b/lib/Sema/AttributeList.cpp index 619a5b961bfe..5a8330bbfd77 100644 --- a/lib/Sema/AttributeList.cpp +++ b/lib/Sema/AttributeList.cpp @@ -107,6 +107,7 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { return llvm::StringSwitch<AttributeList::Kind>(AttrName) .Case("weak", AT_weak) .Case("weakref", AT_weakref) + .Case("objc_arc_weak_reference_unavailable", AT_arc_weakref_unavailable) .Case("pure", AT_pure) .Case("mode", AT_mode) .Case("used", AT_used) @@ -177,6 +178,11 @@ AttributeList::Kind AttributeList::getKind(const IdentifierInfo *Name) { .Case("cf_consumed", AT_cf_consumed) .Case("cf_returns_not_retained", AT_cf_returns_not_retained) .Case("cf_returns_retained", AT_cf_returns_retained) + .Case("cf_returns_autoreleased", AT_cf_returns_autoreleased) + .Case("ns_consumes_self", AT_ns_consumes_self) + .Case("ns_consumed", AT_ns_consumed) + .Case("objc_ownership", AT_objc_ownership) + .Case("objc_precise_lifetime", AT_objc_precise_lifetime) .Case("ownership_returns", AT_ownership_returns) .Case("ownership_holds", AT_ownership_holds) .Case("ownership_takes", AT_ownership_takes) diff --git a/lib/Sema/CMakeLists.txt b/lib/Sema/CMakeLists.txt index 0a670197d7ef..652198189a4c 100644 --- a/lib/Sema/CMakeLists.txt +++ b/lib/Sema/CMakeLists.txt @@ -23,6 +23,7 @@ add_clang_library(clangSema SemaExceptionSpec.cpp SemaExpr.cpp SemaExprCXX.cpp + SemaExprMember.cpp SemaExprObjC.cpp SemaInit.cpp SemaLookup.cpp diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp index 2334ab5128a7..d7dc5b2538c6 100644 --- a/lib/Sema/CodeCompleteConsumer.cpp +++ b/lib/Sema/CodeCompleteConsumer.cpp @@ -46,7 +46,9 @@ bool CodeCompletionContext::wantConstructorResults() const { case CCC_ObjCImplementation: case CCC_ObjCIvarList: case CCC_ClassStructUnion: - case CCC_MemberAccess: + case CCC_DotMemberAccess: + case CCC_ArrowMemberAccess: + case CCC_ObjCPropertyAccess: case CCC_EnumTag: case CCC_UnionTag: case CCC_ClassOrStructTag: @@ -64,6 +66,10 @@ bool CodeCompletionContext::wantConstructorResults() const { case CCC_TypeQualifiers: case CCC_Other: case CCC_OtherWithMacros: + case CCC_ObjCInstanceMessage: + case CCC_ObjCClassMessage: + case CCC_ObjCSuperclass: + case CCC_ObjCCategoryName: return false; } diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp index 5be16e7431c1..c87f2cff5489 100644 --- a/lib/Sema/DeclSpec.cpp +++ b/lib/Sema/DeclSpec.cpp @@ -13,8 +13,10 @@ #include "clang/Parse/ParseDiagnostic.h" // FIXME: remove this back-dependency! #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/LocInfoType.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" #include "clang/AST/NestedNameSpecifier.h" #include "clang/AST/TypeLoc.h" #include "clang/Lex/Preprocessor.h" @@ -124,6 +126,12 @@ void CXXScopeSpec::Adopt(NestedNameSpecifierLoc Other) { Builder.Adopt(Other); } +SourceLocation CXXScopeSpec::getLastQualifierNameLoc() const { + if (!Builder.getRepresentation()) + return SourceLocation(); + return Builder.getTemporary().getLocalBeginLoc(); +} + NestedNameSpecifierLoc CXXScopeSpec::getWithLocInContext(ASTContext &Context) const { if (!Builder.getRepresentation()) @@ -141,6 +149,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, unsigned TypeQuals, bool RefQualifierIsLvalueRef, SourceLocation RefQualifierLoc, + SourceLocation MutableLoc, ExceptionSpecificationType ESpecType, SourceLocation ESpecLoc, @@ -166,6 +175,7 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, I.Fun.ArgInfo = 0; I.Fun.RefQualifierIsLValueRef = RefQualifierIsLvalueRef; I.Fun.RefQualifierLoc = RefQualifierLoc.getRawEncoding(); + I.Fun.MutableLoc = MutableLoc.getRawEncoding(); I.Fun.ExceptionSpecType = ESpecType; I.Fun.ExceptionSpecLoc = ESpecLoc.getRawEncoding(); I.Fun.NumExceptions = 0; @@ -213,6 +223,73 @@ DeclaratorChunk DeclaratorChunk::getFunction(bool hasProto, bool isVariadic, return I; } +bool Declarator::isDeclarationOfFunction() const { + for (unsigned i = 0, i_end = DeclTypeInfo.size(); i < i_end; ++i) { + switch (DeclTypeInfo[i].Kind) { + case DeclaratorChunk::Function: + return true; + case DeclaratorChunk::Paren: + continue; + case DeclaratorChunk::Pointer: + case DeclaratorChunk::Reference: + case DeclaratorChunk::Array: + case DeclaratorChunk::BlockPointer: + case DeclaratorChunk::MemberPointer: + return false; + } + llvm_unreachable("Invalid type chunk"); + return false; + } + + switch (DS.getTypeSpecType()) { + case TST_auto: + case TST_bool: + case TST_char: + case TST_char16: + case TST_char32: + case TST_class: + case TST_decimal128: + case TST_decimal32: + case TST_decimal64: + case TST_double: + case TST_enum: + case TST_error: + case TST_float: + case TST_int: + case TST_struct: + case TST_union: + case TST_unknown_anytype: + case TST_unspecified: + case TST_void: + case TST_wchar: + return false; + + case TST_decltype: + case TST_typeofExpr: + if (Expr *E = DS.getRepAsExpr()) + return E->getType()->isFunctionType(); + return false; + + case TST_underlyingType: + case TST_typename: + case TST_typeofType: { + QualType QT = DS.getRepAsType().get(); + if (QT.isNull()) + return false; + + if (const LocInfoType *LIT = dyn_cast<LocInfoType>(QT)) + QT = LIT->getType(); + + if (QT.isNull()) + return false; + + return QT->isFunctionType(); + } + } + + return false; +} + /// getParsedSpecifiers - Return a bitmask of which flavors of specifiers this /// declaration specifier includes. /// @@ -792,9 +869,6 @@ bool DeclSpec::isMissingDeclaratorOk() { } void UnqualifiedId::clear() { - if (Kind == IK_TemplateId) - TemplateId->Destroy(); - Kind = IK_Identifier; Identifier = 0; StartLocation = SourceLocation(); diff --git a/lib/Sema/DelayedDiagnostic.cpp b/lib/Sema/DelayedDiagnostic.cpp index af548fe13460..c6744ed80d4b 100644 --- a/lib/Sema/DelayedDiagnostic.cpp +++ b/lib/Sema/DelayedDiagnostic.cpp @@ -47,5 +47,8 @@ void DelayedDiagnostic::Destroy() { case Deprecation: delete [] DeprecationData.Message; break; + + case ForbiddenType: + break; } } diff --git a/lib/Sema/JumpDiagnostics.cpp b/lib/Sema/JumpDiagnostics.cpp index ae154aae2062..59bc2637c8f3 100644 --- a/lib/Sema/JumpDiagnostics.cpp +++ b/lib/Sema/JumpDiagnostics.cpp @@ -68,7 +68,10 @@ public: JumpScopeChecker(Stmt *Body, Sema &S); private: void BuildScopeInformation(Decl *D, unsigned &ParentScope); - void BuildScopeInformation(Stmt *S, unsigned ParentScope); + void BuildScopeInformation(VarDecl *D, const BlockDecl *BDecl, + unsigned &ParentScope); + void BuildScopeInformation(Stmt *S, unsigned &origParentScope); + void VerifyJumps(); void VerifyIndirectJumps(); void DiagnoseIndirectJump(IndirectGotoStmt *IG, unsigned IGScope, @@ -87,7 +90,8 @@ JumpScopeChecker::JumpScopeChecker(Stmt *Body, Sema &s) : S(s) { // Build information for the top level compound statement, so that we have a // defined scope record for every "goto" and label. - BuildScopeInformation(Body, 0); + unsigned BodyParentScope = 0; + BuildScopeInformation(Body, BodyParentScope); // Check that all jumps we saw are kosher. VerifyJumps(); @@ -111,87 +115,110 @@ unsigned JumpScopeChecker::GetDeepestCommonScope(unsigned A, unsigned B) { return A; } +typedef std::pair<unsigned,unsigned> ScopePair; + /// GetDiagForGotoScopeDecl - If this decl induces a new goto scope, return a /// diagnostic that should be emitted if control goes over it. If not, return 0. -static std::pair<unsigned,unsigned> - GetDiagForGotoScopeDecl(const Decl *D, bool isCPlusPlus) { +static ScopePair GetDiagForGotoScopeDecl(ASTContext &Context, const Decl *D) { if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { unsigned InDiag = 0, OutDiag = 0; if (VD->getType()->isVariablyModifiedType()) InDiag = diag::note_protected_by_vla; - if (VD->hasAttr<BlocksAttr>()) { - InDiag = diag::note_protected_by___block; - OutDiag = diag::note_exits___block; - } else if (VD->hasAttr<CleanupAttr>()) { - InDiag = diag::note_protected_by_cleanup; - OutDiag = diag::note_exits_cleanup; - } else if (isCPlusPlus) { - if (!VD->hasLocalStorage()) - return std::make_pair(InDiag, OutDiag); - - ASTContext &Context = D->getASTContext(); - QualType T = Context.getBaseElementType(VD->getType()); - if (!T->isDependentType()) { - // C++0x [stmt.dcl]p3: - // A program that jumps from a point where a variable with automatic - // storage duration is not in scope to a point where it is in scope - // is ill-formed unless the variable has scalar type, class type with - // a trivial default constructor and a trivial destructor, a - // cv-qualified version of one of these types, or an array of one of - // the preceding types and is declared without an initializer (8.5). - // Check whether this is a C++ class. - CXXRecordDecl *Record = T->getAsCXXRecordDecl(); - - if (const Expr *Init = VD->getInit()) { - bool CallsTrivialConstructor = false; - if (Record) { - // FIXME: With generalized initializer lists, this may - // classify "X x{};" as having no initializer. - if (const CXXConstructExpr *Construct - = dyn_cast<CXXConstructExpr>(Init)) - if (const CXXConstructorDecl *Constructor - = Construct->getConstructor()) - if ((Context.getLangOptions().CPlusPlus0x - ? Record->hasTrivialDefaultConstructor() - : Record->isPOD()) && - Constructor->isDefaultConstructor()) - CallsTrivialConstructor = true; + if (VD->hasAttr<BlocksAttr>()) + return ScopePair(diag::note_protected_by___block, + diag::note_exits___block); + + if (VD->hasAttr<CleanupAttr>()) + return ScopePair(diag::note_protected_by_cleanup, + diag::note_exits_cleanup); + + if (Context.getLangOptions().ObjCAutoRefCount && VD->hasLocalStorage()) { + switch (VD->getType().getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + return ScopePair(diag::note_protected_by_objc_ownership, + diag::note_exits_objc_ownership); + } + } + + if (Context.getLangOptions().CPlusPlus && VD->hasLocalStorage()) { + // C++0x [stmt.dcl]p3: + // A program that jumps from a point where a variable with automatic + // storage duration is not in scope to a point where it is in scope + // is ill-formed unless the variable has scalar type, class type with + // a trivial default constructor and a trivial destructor, a + // cv-qualified version of one of these types, or an array of one of + // the preceding types and is declared without an initializer. + + // C++03 [stmt.dcl.p3: + // A program that jumps from a point where a local variable + // with automatic storage duration is not in scope to a point + // where it is in scope is ill-formed unless the variable has + // POD type and is declared without an initializer. + + if (const Expr *init = VD->getInit()) { + // We actually give variables of record type (or array thereof) + // an initializer even if that initializer only calls a trivial + // ctor. Detect that case. + // FIXME: With generalized initializer lists, this may + // classify "X x{};" as having no initializer. + unsigned inDiagToUse = diag::note_protected_by_variable_init; + + const CXXRecordDecl *record = 0; + + if (const CXXConstructExpr *cce = dyn_cast<CXXConstructExpr>(init)) { + const CXXConstructorDecl *ctor = cce->getConstructor(); + record = ctor->getParent(); + + if (ctor->isTrivial() && ctor->isDefaultConstructor()) { + if (Context.getLangOptions().CPlusPlus0x) { + inDiagToUse = (record->hasTrivialDestructor() ? 0 : + diag::note_protected_by_variable_nontriv_destructor); + } else { + if (record->isPOD()) + inDiagToUse = 0; + } } - - if (!CallsTrivialConstructor) - InDiag = diag::note_protected_by_variable_init; + } else if (VD->getType()->isArrayType()) { + record = VD->getType()->getBaseElementTypeUnsafe() + ->getAsCXXRecordDecl(); } - - // Note whether we have a class with a non-trivial destructor. - if (Record && !Record->hasTrivialDestructor()) + + if (inDiagToUse) + InDiag = inDiagToUse; + + // Also object to indirect jumps which leave scopes with dtors. + if (record && !record->hasTrivialDestructor()) OutDiag = diag::note_exits_dtor; } } - return std::make_pair(InDiag, OutDiag); + return ScopePair(InDiag, OutDiag); } if (const TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { if (TD->getUnderlyingType()->isVariablyModifiedType()) - return std::make_pair((unsigned) diag::note_protected_by_vla_typedef, 0); + return ScopePair(diag::note_protected_by_vla_typedef, 0); } if (const TypeAliasDecl *TD = dyn_cast<TypeAliasDecl>(D)) { if (TD->getUnderlyingType()->isVariablyModifiedType()) - return std::make_pair((unsigned) diag::note_protected_by_vla_type_alias, 0); + return ScopePair(diag::note_protected_by_vla_type_alias, 0); } - return std::make_pair(0U, 0U); + return ScopePair(0U, 0U); } /// \brief Build scope information for a declaration that is part of a DeclStmt. void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) { - bool isCPlusPlus = this->S.getLangOptions().CPlusPlus; - // If this decl causes a new scope, push and switch to it. - std::pair<unsigned,unsigned> Diags - = GetDiagForGotoScopeDecl(D, isCPlusPlus); + std::pair<unsigned,unsigned> Diags = GetDiagForGotoScopeDecl(S.Context, D); if (Diags.first || Diags.second) { Scopes.push_back(GotoScope(ParentScope, Diags.first, Diags.second, D->getLocation())); @@ -205,11 +232,55 @@ void JumpScopeChecker::BuildScopeInformation(Decl *D, unsigned &ParentScope) { BuildScopeInformation(Init, ParentScope); } +/// \brief Build scope information for a captured block literal variables. +void JumpScopeChecker::BuildScopeInformation(VarDecl *D, + const BlockDecl *BDecl, + unsigned &ParentScope) { + // exclude captured __block variables; there's no destructor + // associated with the block literal for them. + if (D->hasAttr<BlocksAttr>()) + return; + QualType T = D->getType(); + QualType::DestructionKind destructKind = T.isDestructedType(); + if (destructKind != QualType::DK_none) { + std::pair<unsigned,unsigned> Diags; + switch (destructKind) { + case QualType::DK_cxx_destructor: + Diags = ScopePair(diag::note_enters_block_captures_cxx_obj, + diag::note_exits_block_captures_cxx_obj); + break; + case QualType::DK_objc_strong_lifetime: + Diags = ScopePair(diag::note_enters_block_captures_strong, + diag::note_exits_block_captures_strong); + break; + case QualType::DK_objc_weak_lifetime: + Diags = ScopePair(diag::note_enters_block_captures_weak, + diag::note_exits_block_captures_weak); + break; + case QualType::DK_none: + llvm_unreachable("no-liftime captured variable"); + } + SourceLocation Loc = D->getLocation(); + if (Loc.isInvalid()) + Loc = BDecl->getLocation(); + Scopes.push_back(GotoScope(ParentScope, + Diags.first, Diags.second, Loc)); + ParentScope = Scopes.size()-1; + } +} + /// BuildScopeInformation - The statements from CI to CE are known to form a /// coherent VLA scope with a specified parent node. Walk through the /// statements, adding any labels or gotos to LabelAndGotoScopes and recursively /// walking the AST as needed. -void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { +void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned &origParentScope) { + // If this is a statement, rather than an expression, scopes within it don't + // propagate out into the enclosing scope. Otherwise we have to worry + // about block literals, which have the lifetime of their enclosing statement. + unsigned independentParentScope = origParentScope; + unsigned &ParentScope = ((isa<Expr>(S) && !isa<StmtExpr>(S)) + ? origParentScope : independentParentScope); + bool SkipFirstSubStmt = false; // If we found a label, remember that it is in ParentScope scope. @@ -291,17 +362,17 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { BuildScopeInformation(*I, ParentScope); continue; } - // Disallow jumps into any part of an @try statement by pushing a scope and // walking all sub-stmts in that scope. if (ObjCAtTryStmt *AT = dyn_cast<ObjCAtTryStmt>(SubStmt)) { + unsigned newParentScope; // Recursively walk the AST for the @try part. Scopes.push_back(GotoScope(ParentScope, diag::note_protected_by_objc_try, diag::note_exits_objc_try, AT->getAtTryLoc())); if (Stmt *TryPart = AT->getTryBody()) - BuildScopeInformation(TryPart, Scopes.size()-1); + BuildScopeInformation(TryPart, (newParentScope = Scopes.size()-1)); // Jump from the catch to the finally or try is not valid. for (unsigned I = 0, N = AT->getNumCatchStmts(); I != N; ++I) { @@ -311,7 +382,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { diag::note_exits_objc_catch, AC->getAtCatchLoc())); // @catches are nested and it isn't - BuildScopeInformation(AC->getCatchBody(), Scopes.size()-1); + BuildScopeInformation(AC->getCatchBody(), + (newParentScope = Scopes.size()-1)); } // Jump from the finally to the try or catch is not valid. @@ -320,12 +392,13 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { diag::note_protected_by_objc_finally, diag::note_exits_objc_finally, AF->getAtFinallyLoc())); - BuildScopeInformation(AF, Scopes.size()-1); + BuildScopeInformation(AF, (newParentScope = Scopes.size()-1)); } continue; } - + + unsigned newParentScope; // Disallow jumps into the protected statement of an @synchronized, but // allow jumps into the object expression it protects. if (ObjCAtSynchronizedStmt *AS = dyn_cast<ObjCAtSynchronizedStmt>(SubStmt)){ @@ -339,7 +412,8 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { diag::note_protected_by_objc_synchronized, diag::note_exits_objc_synchronized, AS->getAtSynchronizedLoc())); - BuildScopeInformation(AS->getSynchBody(), Scopes.size()-1); + BuildScopeInformation(AS->getSynchBody(), + (newParentScope = Scopes.size()-1)); continue; } @@ -351,7 +425,7 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { diag::note_exits_cxx_try, TS->getSourceRange().getBegin())); if (Stmt *TryBlock = TS->getTryBlock()) - BuildScopeInformation(TryBlock, Scopes.size()-1); + BuildScopeInformation(TryBlock, (newParentScope = Scopes.size()-1)); // Jump from the catch into the try is not allowed either. for (unsigned I = 0, E = TS->getNumHandlers(); I != E; ++I) { @@ -360,12 +434,34 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S, unsigned ParentScope) { diag::note_protected_by_cxx_catch, diag::note_exits_cxx_catch, CS->getSourceRange().getBegin())); - BuildScopeInformation(CS->getHandlerBlock(), Scopes.size()-1); + BuildScopeInformation(CS->getHandlerBlock(), + (newParentScope = Scopes.size()-1)); } continue; } + // Disallow jumps into the protected statement of an @autoreleasepool. + if (ObjCAutoreleasePoolStmt *AS = dyn_cast<ObjCAutoreleasePoolStmt>(SubStmt)){ + // Recursively walk the AST for the @autoreleasepool part, protected by a new + // scope. + Scopes.push_back(GotoScope(ParentScope, + diag::note_protected_by_objc_autoreleasepool, + diag::note_exits_objc_autoreleasepool, + AS->getAtLoc())); + BuildScopeInformation(AS->getSubStmt(), (newParentScope = Scopes.size()-1)); + continue; + } + + if (const BlockExpr *BE = dyn_cast<BlockExpr>(SubStmt)) { + const BlockDecl *BDecl = BE->getBlockDecl(); + for (BlockDecl::capture_const_iterator ci = BDecl->capture_begin(), + ce = BDecl->capture_end(); ci != ce; ++ci) { + VarDecl *variable = ci->getVariable(); + BuildScopeInformation(variable, BDecl, ParentScope); + } + } + // Recursively walk the AST. BuildScopeInformation(SubStmt, ParentScope); } diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index 8297b3155a8c..fdf3bb3cb012 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -141,9 +141,9 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, : TheTargetAttributesSema(0), FPFeatures(pp.getLangOptions()), LangOpts(pp.getLangOptions()), PP(pp), Context(ctxt), Consumer(consumer), Diags(PP.getDiagnostics()), SourceMgr(PP.getSourceManager()), - ExternalSource(0), CodeCompleter(CodeCompleter), CurContext(0), - PackContext(0), MSStructPragmaOn(false), VisContext(0), - LateTemplateParser(0), OpaqueParser(0), + CollectStats(false), ExternalSource(0), CodeCompleter(CodeCompleter), + CurContext(0), PackContext(0), MSStructPragmaOn(false), VisContext(0), + ExprNeedsCleanups(0), LateTemplateParser(0), OpaqueParser(0), IdResolver(pp.getLangOptions()), CXXTypeInfoDecl(0), MSVCGuidDecl(0), GlobalNewDeleteDeclared(false), CompleteTranslationUnit(CompleteTranslationUnit), @@ -154,6 +154,8 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, AnalysisWarnings(*this) { TUScope = 0; + LoadedExternalKnownNamespaces = false; + if (getLangOptions().CPlusPlus) FieldCollector.reset(new CXXFieldCollector()); @@ -162,7 +164,7 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer, &Context); ExprEvalContexts.push_back( - ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0)); + ExpressionEvaluationContextRecord(PotentiallyEvaluated, 0, false)); FunctionScopes.push_back(new FunctionScopeInfo(Diags)); } @@ -202,22 +204,61 @@ Sema::~Sema() { ExternalSema->ForgetSema(); } + +/// makeUnavailableInSystemHeader - There is an error in the current +/// context. If we're still in a system header, and we can plausibly +/// make the relevant declaration unavailable instead of erroring, do +/// so and return true. +bool Sema::makeUnavailableInSystemHeader(SourceLocation loc, + llvm::StringRef msg) { + // If we're not in a function, it's an error. + FunctionDecl *fn = dyn_cast<FunctionDecl>(CurContext); + if (!fn) return false; + + // If we're in template instantiation, it's an error. + if (!ActiveTemplateInstantiations.empty()) + return false; + + // If that function's not in a system header, it's an error. + if (!Context.getSourceManager().isInSystemHeader(loc)) + return false; + + // If the function is already unavailable, it's not an error. + if (fn->hasAttr<UnavailableAttr>()) return true; + + fn->addAttr(new (Context) UnavailableAttr(loc, Context, msg)); + return true; +} + ASTMutationListener *Sema::getASTMutationListener() const { return getASTConsumer().GetASTMutationListener(); } +/// \brief Print out statistics about the semantic analysis. +void Sema::PrintStats() const { + llvm::errs() << "\n*** Semantic Analysis Stats:\n"; + llvm::errs() << NumSFINAEErrors << " SFINAE diagnostics trapped.\n"; + + BumpAlloc.PrintStats(); + AnalysisWarnings.PrintStats(); +} + /// ImpCastExprToType - If Expr is not of type 'Type', insert an implicit cast. /// If there is already an implicit cast, merge into the existing one. /// The result is of the given category. ExprResult Sema::ImpCastExprToType(Expr *E, QualType Ty, CastKind Kind, ExprValueKind VK, - const CXXCastPath *BasePath) { + const CXXCastPath *BasePath, + CheckedConversionKind CCK) { QualType ExprTy = Context.getCanonicalType(E->getType()); QualType TypeTy = Context.getCanonicalType(Ty); if (ExprTy == TypeTy) return Owned(E); + if (getLangOptions().ObjCAutoRefCount) + CheckObjCARCConversion(SourceRange(), Ty, E, CCK); + // If this is a derived-to-base cast to a through a virtual base, we // need a vtable. if (Kind == CK_DerivedToBase && @@ -729,8 +770,8 @@ void Sema::PopFunctionOrBlockScope(const AnalysisBasedWarnings::Policy *WP, /// \brief Determine whether any errors occurred within this function/method/ /// block. -bool Sema::hasAnyErrorsInThisFunction() const { - return getCurFunction()->ErrorTrap.hasErrorOccurred(); +bool Sema::hasAnyUnrecoverableErrorsInThisFunction() const { + return getCurFunction()->ErrorTrap.hasUnrecoverableErrorOccurred(); } BlockScopeInfo *Sema::getCurBlock() { @@ -748,6 +789,10 @@ ExternalSemaSource::ReadMethodPool(Selector Sel) { return std::pair<ObjCMethodList, ObjCMethodList>(); } +void ExternalSemaSource::ReadKnownNamespaces( + llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces) { +} + void PrettyDeclStackTraceEntry::print(llvm::raw_ostream &OS) const { SourceLocation Loc = this->Loc; if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation(); diff --git a/lib/Sema/SemaCXXCast.cpp b/lib/Sema/SemaCXXCast.cpp index e46ad5bcfeb5..d053d5a9e9fe 100644 --- a/lib/Sema/SemaCXXCast.cpp +++ b/lib/Sema/SemaCXXCast.cpp @@ -63,7 +63,8 @@ static void CheckDynamicCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, CastKind &Kind, CXXCastPath &BasePath); -static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType); +static bool CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType, + bool CheckCVR, bool CheckObjCLifetime); // The Try functions attempt a specific way of casting. If they succeed, they // return TC_Success. If their way of casting is not appropriate for the given @@ -109,12 +110,14 @@ static TryCastResult TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExp CXXCastPath &BasePath); static TryCastResult TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, - QualType DestType, bool CStyle, + QualType DestType, + Sema::CheckedConversionKind CCK, const SourceRange &OpRange, unsigned &msg, CastKind &Kind); static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, - QualType DestType, bool CStyle, + QualType DestType, + Sema::CheckedConversionKind CCK, const SourceRange &OpRange, unsigned &msg, CastKind &Kind, @@ -131,17 +134,23 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, /// ActOnCXXNamedCast - Parse {dynamic,static,reinterpret,const}_cast's. ExprResult Sema::ActOnCXXNamedCast(SourceLocation OpLoc, tok::TokenKind Kind, - SourceLocation LAngleBracketLoc, ParsedType Ty, + SourceLocation LAngleBracketLoc, Declarator &D, SourceLocation RAngleBracketLoc, SourceLocation LParenLoc, Expr *E, SourceLocation RParenLoc) { - - TypeSourceInfo *DestTInfo; - QualType DestType = GetTypeFromParser(Ty, &DestTInfo); - if (!DestTInfo) - DestTInfo = Context.getTrivialTypeSourceInfo(DestType, SourceLocation()); - return BuildCXXNamedCast(OpLoc, Kind, DestTInfo, move(E), + assert(!D.isInvalidType()); + + TypeSourceInfo *TInfo = GetTypeForDeclaratorCast(D, E->getType()); + if (D.isInvalidType()) + return ExprError(); + + if (getLangOptions().CPlusPlus) { + // Check that there are no default arguments (C++ only). + CheckExtraCXXDefaultArguments(D); + } + + return BuildCXXNamedCast(OpLoc, Kind, TInfo, move(E), SourceRange(LAngleBracketLoc, RAngleBracketLoc), SourceRange(LParenLoc, RParenLoc)); } @@ -248,8 +257,10 @@ static bool tryDiagnoseOverloadedCast(Sema &S, CastType CT, InitializedEntity entity = InitializedEntity::InitializeTemporary(destType); InitializationKind initKind - = InitializationKind::CreateCast(/*type range?*/ range, - (CT == CT_CStyle || CT == CT_Functional)); + = (CT == CT_CStyle)? InitializationKind::CreateCStyleCast(range.getBegin(), + range) + : (CT == CT_Functional)? InitializationKind::CreateFunctionalCast(range) + : InitializationKind::CreateCast(/*type range?*/ range); InitializationSequence sequence(S, entity, initKind, &src, 1); assert(sequence.Failed() && "initialization succeeded on second try?"); @@ -373,8 +384,19 @@ static bool UnwrapDissimilarPointerTypes(QualType& T1, QualType& T2) { /// DestType casts away constness as defined in C++ 5.2.11p8ff. This is used by /// the cast checkers. Both arguments must denote pointer (possibly to member) /// types. +/// +/// \param CheckCVR Whether to check for const/volatile/restrict qualifiers. +/// +/// \param CheckObjCLifetime Whether to check Objective-C lifetime qualifiers. static bool -CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { +CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType, + bool CheckCVR, bool CheckObjCLifetime) { + // If the only checking we care about is for Objective-C lifetime qualifiers, + // and we're not in ARC mode, there's nothing to check. + if (!CheckCVR && CheckObjCLifetime && + !Self.Context.getLangOptions().ObjCAutoRefCount) + return false; + // Casting away constness is defined in C++ 5.2.11p8 with reference to // C++ 4.4. We piggyback on Sema::IsQualificationConversion for this, since // the rules are non-trivial. So first we construct Tcv *...cv* as described @@ -394,13 +416,23 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { // purpose of this check, because other qualifiers (address spaces, // Objective-C GC, etc.) are part of the type's identity. while (UnwrapDissimilarPointerTypes(UnwrappedSrcType, UnwrappedDestType)) { - Qualifiers SrcQuals; + // Determine the relevant qualifiers at this level. + Qualifiers SrcQuals, DestQuals; Self.Context.getUnqualifiedArrayType(UnwrappedSrcType, SrcQuals); - cv1.push_back(Qualifiers::fromCVRMask(SrcQuals.getCVRQualifiers())); - - Qualifiers DestQuals; Self.Context.getUnqualifiedArrayType(UnwrappedDestType, DestQuals); - cv2.push_back(Qualifiers::fromCVRMask(DestQuals.getCVRQualifiers())); + + Qualifiers RetainedSrcQuals, RetainedDestQuals; + if (CheckCVR) { + RetainedSrcQuals.setCVRQualifiers(SrcQuals.getCVRQualifiers()); + RetainedDestQuals.setCVRQualifiers(DestQuals.getCVRQualifiers()); + } + + if (CheckObjCLifetime && + !DestQuals.compatiblyIncludesObjCLifetime(SrcQuals)) + return true; + + cv1.push_back(RetainedSrcQuals); + cv2.push_back(RetainedDestQuals); } if (cv1.empty()) return false; @@ -420,8 +452,10 @@ CastsAwayConstness(Sema &Self, QualType SrcType, QualType DestType) { } // Test if they're compatible. + bool ObjCLifetimeConversion; return SrcConstruct != DestConstruct && - !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false); + !Self.IsQualificationConversion(SrcConstruct, DestConstruct, false, + ObjCLifetimeConversion); } /// CheckDynamicCast - Check that a dynamic_cast\<DestType\>(SrcExpr) is valid. @@ -595,9 +629,10 @@ CheckReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } unsigned msg = diag::err_bad_cxx_cast_generic; - if (TryReinterpretCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, - msg, Kind) - != TC_Success && msg != 0) + TryCastResult tcr = + TryReinterpretCast(Self, SrcExpr, DestType, + /*CStyle*/false, OpRange, msg, Kind); + if (tcr != TC_Success && msg != 0) { if (SrcExpr.isInvalid()) // if conversion failed, don't report another error return; @@ -611,7 +646,12 @@ CheckReinterpretCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } else { diagnoseBadCast(Self, msg, CT_Reinterpret, OpRange, SrcExpr.get(), DestType); } - } + } else if (tcr == TC_Success && Self.getLangOptions().ObjCAutoRefCount) { + Expr *Exp = SrcExpr.get(); + // Note that Exp does not change with CCK_OtherCast cast type + Self.CheckObjCARCConversion(OpRange, DestType, + Exp, Sema::CCK_OtherCast); + } } @@ -654,8 +694,10 @@ CheckStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } unsigned msg = diag::err_bad_cxx_cast_generic; - if (TryStaticCast(Self, SrcExpr, DestType, /*CStyle*/false, OpRange, msg, - Kind, BasePath) != TC_Success && msg != 0) { + TryCastResult tcr + = TryStaticCast(Self, SrcExpr, DestType, Sema::CCK_OtherCast, OpRange, msg, + Kind, BasePath); + if (tcr != TC_Success && msg != 0) { if (SrcExpr.isInvalid()) return; if (SrcExpr.get()->getType() == Self.Context.OverloadTy) { @@ -667,6 +709,15 @@ CheckStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, } else { diagnoseBadCast(Self, msg, CT_Static, OpRange, SrcExpr.get(), DestType); } + } else if (tcr == TC_Success) { + if (Kind == CK_BitCast) + Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange); + if (Self.getLangOptions().ObjCAutoRefCount) { + Expr *Exp = SrcExpr.get(); + // Note that Exp does not change with CCK_OtherCast cast type + Self.CheckObjCARCConversion(OpRange, DestType, + Exp, Sema::CCK_OtherCast); + } } else if (Kind == CK_BitCast) Self.CheckCastAlign(SrcExpr.get(), DestType, OpRange); @@ -676,10 +727,15 @@ CheckStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, /// possible. If @p CStyle, ignore access restrictions on hierarchy casting /// and casting away constness. static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, - QualType DestType, bool CStyle, + QualType DestType, + Sema::CheckedConversionKind CCK, const SourceRange &OpRange, unsigned &msg, CastKind &Kind, CXXCastPath &BasePath) { + // Determine whether we have the semantics of a C-style cast. + bool CStyle + = (CCK == Sema::CCK_CStyleCast || CCK == Sema::CCK_FunctionalCast); + // The order the tests is not entirely arbitrary. There is one conversion // that can be handled in two different ways. Given: // struct A {}; @@ -715,7 +771,7 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, // C++ 5.2.9p2: An expression e can be explicitly converted to a type T // [...] if the declaration "T t(e);" is well-formed, [...]. - tcr = TryStaticImplicitCast(Self, SrcExpr, DestType, CStyle, OpRange, msg, + tcr = TryStaticImplicitCast(Self, SrcExpr, DestType, CCK, OpRange, msg, Kind); if (SrcExpr.isInvalid()) return TC_Failed; @@ -792,10 +848,20 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr, QualType DestPointee = DestPointer->getPointeeType(); if (DestPointee->isIncompleteOrObjectType()) { // This is definitely the intended conversion, but it might fail due - // to a const violation. - if (!CStyle && !DestPointee.isAtLeastAsQualifiedAs(SrcPointee)) { - msg = diag::err_bad_cxx_cast_qualifiers_away; - return TC_Failed; + // to a qualifier violation. Note that we permit Objective-C lifetime + // and GC qualifier mismatches here. + if (!CStyle) { + Qualifiers DestPointeeQuals = DestPointee.getQualifiers(); + Qualifiers SrcPointeeQuals = SrcPointee.getQualifiers(); + DestPointeeQuals.removeObjCGCAttr(); + DestPointeeQuals.removeObjCLifetime(); + SrcPointeeQuals.removeObjCGCAttr(); + SrcPointeeQuals.removeObjCLifetime(); + if (DestPointeeQuals != SrcPointeeQuals && + !DestPointeeQuals.compatiblyIncludes(SrcPointeeQuals)) { + msg = diag::err_bad_cxx_cast_qualifiers_away; + return TC_Failed; + } } Kind = CK_BitCast; return TC_Success; @@ -845,6 +911,7 @@ TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType, // FIXME: Should allow casting away constness if CStyle. bool DerivedToBase; bool ObjCConversion; + bool ObjCLifetimeConversion; QualType FromType = SrcExpr->getType(); QualType ToType = R->getPointeeType(); if (CStyle) { @@ -854,8 +921,9 @@ TryLValueToRValueCast(Sema &Self, Expr *SrcExpr, QualType DestType, if (Self.CompareReferenceRelationship(SrcExpr->getLocStart(), ToType, FromType, - DerivedToBase, ObjCConversion) < - Sema::Ref_Compatible_With_Added_Qualification) { + DerivedToBase, ObjCConversion, + ObjCLifetimeConversion) + < Sema::Ref_Compatible_With_Added_Qualification) { msg = diag::err_bad_lvalue_to_rvalue_cast; return TC_Failed; } @@ -1172,7 +1240,8 @@ TryStaticMemberPointerUpcast(Sema &Self, ExprResult &SrcExpr, QualType SrcType, /// @c static_cast if the declaration "T t(e);" is well-formed [...]. TryCastResult TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, - bool CStyle, const SourceRange &OpRange, unsigned &msg, + Sema::CheckedConversionKind CCK, + const SourceRange &OpRange, unsigned &msg, CastKind &Kind) { if (DestType->isRecordType()) { if (Self.RequireCompleteType(OpRange.getBegin(), DestType, @@ -1184,7 +1253,11 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, InitializedEntity Entity = InitializedEntity::InitializeTemporary(DestType); InitializationKind InitKind - = InitializationKind::CreateCast(/*FIXME:*/OpRange, CStyle); + = (CCK == Sema::CCK_CStyleCast) + ? InitializationKind::CreateCStyleCast(OpRange.getBegin(), OpRange) + : (CCK == Sema::CCK_FunctionalCast) + ? InitializationKind::CreateFunctionalCast(OpRange) + : InitializationKind::CreateCast(OpRange); Expr *SrcExprRaw = SrcExpr.get(); InitializationSequence InitSeq(Self, Entity, InitKind, &SrcExprRaw, 1); @@ -1193,7 +1266,8 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, // There is no other way that works. // On the other hand, if we're checking a C-style cast, we've still got // the reinterpret_cast way. - + bool CStyle + = (CCK == Sema::CCK_CStyleCast || CCK == Sema::CCK_FunctionalCast); if (InitSeq.Failed() && (CStyle || !DestType->isReferenceType())) return TC_NotApplicable; @@ -1428,7 +1502,8 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, // constness. // A reinterpret_cast followed by a const_cast can, though, so in C-style, // we accept it. - if (!CStyle && CastsAwayConstness(Self, SrcType, DestType)) { + if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle, + /*CheckObjCLifetime=*/CStyle)) { msg = diag::err_bad_cxx_cast_qualifiers_away; return TC_Failed; } @@ -1543,7 +1618,8 @@ static TryCastResult TryReinterpretCast(Sema &Self, ExprResult &SrcExpr, // C++ 5.2.10p2: The reinterpret_cast operator shall not cast away constness. // The C-style cast operator can. - if (!CStyle && CastsAwayConstness(Self, SrcType, DestType)) { + if (CastsAwayConstness(Self, SrcType, DestType, /*CheckCVR=*/!CStyle, + /*CheckObjCLifetime=*/CStyle)) { msg = diag::err_bad_cxx_cast_qualifiers_away; return TC_Failed; } @@ -1675,11 +1751,14 @@ Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, if (tcr == TC_Success) Kind = CK_NoOp; + Sema::CheckedConversionKind CCK + = FunctionalStyle? Sema::CCK_FunctionalCast + : Sema::CCK_CStyleCast; if (tcr == TC_NotApplicable) { // ... or if that is not possible, a static_cast, ignoring const, ... ExprResult CastExprRes = Owned(CastExpr); - tcr = TryStaticCast(*this, CastExprRes, CastTy, /*CStyle*/true, R, msg, - Kind, BasePath); + tcr = TryStaticCast(*this, CastExprRes, CastTy, CCK, R, msg, Kind, + BasePath); if (CastExprRes.isInvalid()) return ExprError(); CastExpr = CastExprRes.take(); @@ -1694,6 +1773,9 @@ Sema::CXXCheckCStyleCast(SourceRange R, QualType CastTy, ExprValueKind &VK, } } + if (getLangOptions().ObjCAutoRefCount && tcr == TC_Success) + CheckObjCARCConversion(R, CastTy, CastExpr, CCK); + if (tcr != TC_Success && msg != 0) { if (CastExpr->getType() == Context.OverloadTy) { DeclAccessPair Found; diff --git a/lib/Sema/SemaCXXScopeSpec.cpp b/lib/Sema/SemaCXXScopeSpec.cpp index 61d9e93f2ff5..5f8c9c62a409 100644 --- a/lib/Sema/SemaCXXScopeSpec.cpp +++ b/lib/Sema/SemaCXXScopeSpec.cpp @@ -211,25 +211,40 @@ bool Sema::RequireCompleteDeclContext(CXXScopeSpec &SS, DeclContext *DC) { assert(DC != 0 && "given null context"); - if (TagDecl *Tag = dyn_cast<TagDecl>(DC)) { + if (TagDecl *tag = dyn_cast<TagDecl>(DC)) { // If this is a dependent type, then we consider it complete. - if (Tag->isDependentContext()) + if (tag->isDependentContext()) return false; // If we're currently defining this type, then lookup into the // type is okay: don't complain that it isn't complete yet. - const TagType *TagT = Context.getTypeDeclType(Tag)->getAs<TagType>(); - if (TagT && TagT->isBeingDefined()) + QualType type = Context.getTypeDeclType(tag); + const TagType *tagType = type->getAs<TagType>(); + if (tagType && tagType->isBeingDefined()) return false; + SourceLocation loc = SS.getLastQualifierNameLoc(); + if (loc.isInvalid()) loc = SS.getRange().getBegin(); + // The type must be complete. - if (RequireCompleteType(SS.getRange().getBegin(), - Context.getTypeDeclType(Tag), + if (RequireCompleteType(loc, type, PDiag(diag::err_incomplete_nested_name_spec) << SS.getRange())) { SS.SetInvalid(SS.getRange()); return true; } + + // Fixed enum types are complete, but they aren't valid as scopes + // until we see a definition, so awkwardly pull out this special + // case. + if (const EnumType *enumType = dyn_cast_or_null<EnumType>(tagType)) { + if (!enumType->getDecl()->isDefinition()) { + Diag(loc, diag::err_incomplete_nested_name_spec) + << type << SS.getRange(); + SS.SetInvalid(SS.getRange()); + return true; + } + } } return false; @@ -464,26 +479,29 @@ bool Sema::BuildCXXNestedNameSpecifier(Scope *S, // We haven't found anything, and we're not recovering from a // different kind of error, so look for typos. DeclarationName Name = Found.getLookupName(); - if (CorrectTypo(Found, S, &SS, LookupCtx, EnteringContext, - CTC_NoKeywords) && - Found.isSingleResult() && - isAcceptableNestedNameSpecifier(Found.getAsSingle<NamedDecl>())) { + TypoCorrection Corrected; + Found.clear(); + if ((Corrected = CorrectTypo(Found.getLookupNameInfo(), + Found.getLookupKind(), S, &SS, LookupCtx, + EnteringContext, CTC_NoKeywords)) && + isAcceptableNestedNameSpecifier(Corrected.getCorrectionDecl())) { + std::string CorrectedStr(Corrected.getAsString(getLangOptions())); + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions())); if (LookupCtx) Diag(Found.getNameLoc(), diag::err_no_member_suggest) - << Name << LookupCtx << Found.getLookupName() << SS.getRange() - << FixItHint::CreateReplacement(Found.getNameLoc(), - Found.getLookupName().getAsString()); + << Name << LookupCtx << CorrectedQuotedStr << SS.getRange() + << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr); else Diag(Found.getNameLoc(), diag::err_undeclared_var_use_suggest) - << Name << Found.getLookupName() - << FixItHint::CreateReplacement(Found.getNameLoc(), - Found.getLookupName().getAsString()); + << Name << CorrectedQuotedStr + << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr); - if (NamedDecl *ND = Found.getAsSingle<NamedDecl>()) - Diag(ND->getLocation(), diag::note_previous_decl) - << ND->getDeclName(); + if (NamedDecl *ND = Corrected.getCorrectionDecl()) { + Diag(ND->getLocation(), diag::note_previous_decl) << CorrectedQuotedStr; + Found.addDecl(ND); + } + Found.setLookupName(Corrected.getCorrection()); } else { - Found.clear(); Found.setLookupName(&Identifier); } } diff --git a/lib/Sema/SemaChecking.cpp b/lib/Sema/SemaChecking.cpp index 05c257ab8332..690a29d281d2 100644 --- a/lib/Sema/SemaChecking.cpp +++ b/lib/Sema/SemaChecking.cpp @@ -22,6 +22,7 @@ #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/StmtCXX.h" #include "clang/AST/StmtObjC.h" @@ -319,12 +320,41 @@ bool Sema::CheckFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall) { } // Memset/memcpy/memmove handling - if (FDecl->getLinkage() == ExternalLinkage && - (!getLangOptions().CPlusPlus || FDecl->isExternC())) { - if (FnInfo->isStr("memset") || FnInfo->isStr("memcpy") || - FnInfo->isStr("memmove")) - CheckMemsetcpymoveArguments(TheCall, FnInfo); + int CMF = -1; + switch (FDecl->getBuiltinID()) { + case Builtin::BI__builtin_memset: + case Builtin::BI__builtin___memset_chk: + case Builtin::BImemset: + CMF = CMF_Memset; + break; + + case Builtin::BI__builtin_memcpy: + case Builtin::BI__builtin___memcpy_chk: + case Builtin::BImemcpy: + CMF = CMF_Memcpy; + break; + + case Builtin::BI__builtin_memmove: + case Builtin::BI__builtin___memmove_chk: + case Builtin::BImemmove: + CMF = CMF_Memmove; + break; + + default: + if (FDecl->getLinkage() == ExternalLinkage && + (!getLangOptions().CPlusPlus || FDecl->isExternC())) { + if (FnInfo->isStr("memset")) + CMF = CMF_Memset; + else if (FnInfo->isStr("memcpy")) + CMF = CMF_Memcpy; + else if (FnInfo->isStr("memmove")) + CMF = CMF_Memmove; + } + break; } + + if (CMF != -1) + CheckMemsetcpymoveArguments(TheCall, CheckedMemoryFunction(CMF), FnInfo); return false; } @@ -382,14 +412,14 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { // casts here. // FIXME: We don't allow floating point scalars as input. Expr *FirstArg = TheCall->getArg(0); - if (!FirstArg->getType()->isPointerType()) { + const PointerType *pointerType = FirstArg->getType()->getAs<PointerType>(); + if (!pointerType) { Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer) << FirstArg->getType() << FirstArg->getSourceRange(); return ExprError(); } - QualType ValType = - FirstArg->getType()->getAs<PointerType>()->getPointeeType(); + QualType ValType = pointerType->getPointeeType(); if (!ValType->isIntegerType() && !ValType->isAnyPointerType() && !ValType->isBlockPointerType()) { Diag(DRE->getLocStart(), diag::err_atomic_builtin_must_be_pointer_intptr) @@ -397,6 +427,20 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { return ExprError(); } + switch (ValType.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + // okay + break; + + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Autoreleasing: + Diag(DRE->getLocStart(), diag::err_arc_atomic_ownership) + << ValType << FirstArg->getSourceRange(); + return ExprError(); + } + // The majority of builtins return a value, but a few have special return // types, so allow them to override appropriately below. QualType ResultType = ValType; @@ -518,7 +562,8 @@ Sema::SemaBuiltinAtomicOverloaded(ExprResult TheCallResult) { CastKind Kind = CK_Invalid; ExprValueKind VK = VK_RValue; CXXCastPath BasePath; - Arg = CheckCastTypes(Arg.get()->getSourceRange(), ValType, Arg.take(), Kind, VK, BasePath); + Arg = CheckCastTypes(Arg.get()->getLocStart(), Arg.get()->getSourceRange(), + ValType, Arg.take(), Kind, VK, BasePath); if (Arg.isInvalid()) return ExprError(); @@ -1812,6 +1857,27 @@ static bool isDynamicClassType(QualType T) { return false; } +/// \brief If E is a sizeof expression, returns its argument expression, +/// otherwise returns NULL. +static const Expr *getSizeOfExprArg(const Expr* E) { + if (const UnaryExprOrTypeTraitExpr *SizeOf = + dyn_cast<UnaryExprOrTypeTraitExpr>(E)) + if (SizeOf->getKind() == clang::UETT_SizeOf && !SizeOf->isArgumentType()) + return SizeOf->getArgumentExpr()->IgnoreParenImpCasts(); + + return 0; +} + +/// \brief If E is a sizeof expression, returns its argument type. +static QualType getSizeOfArgType(const Expr* E) { + if (const UnaryExprOrTypeTraitExpr *SizeOf = + dyn_cast<UnaryExprOrTypeTraitExpr>(E)) + if (SizeOf->getKind() == clang::UETT_SizeOf) + return SizeOf->getTypeOfArgument(); + + return QualType(); +} + /// \brief Check for dangerous or invalid arguments to memset(). /// /// This issues warnings on known problematic, dangerous or unspecified @@ -1819,35 +1885,95 @@ static bool isDynamicClassType(QualType T) { /// /// \param Call The call expression to diagnose. void Sema::CheckMemsetcpymoveArguments(const CallExpr *Call, - const IdentifierInfo *FnName) { + CheckedMemoryFunction CMF, + IdentifierInfo *FnName) { // It is possible to have a non-standard definition of memset. Validate - // we have the proper number of arguments, and if not, abort further - // checking. - if (Call->getNumArgs() != 3) + // we have enough arguments, and if not, abort further checking. + if (Call->getNumArgs() < 3) return; - unsigned LastArg = FnName->isStr("memset")? 1 : 2; + unsigned LastArg = (CMF == CMF_Memset? 1 : 2); + const Expr *LenExpr = Call->getArg(2)->IgnoreParenImpCasts(); + + // We have special checking when the length is a sizeof expression. + QualType SizeOfArgTy = getSizeOfArgType(LenExpr); + const Expr *SizeOfArg = getSizeOfExprArg(LenExpr); + llvm::FoldingSetNodeID SizeOfArgID; + for (unsigned ArgIdx = 0; ArgIdx != LastArg; ++ArgIdx) { const Expr *Dest = Call->getArg(ArgIdx)->IgnoreParenImpCasts(); + SourceRange ArgRange = Call->getArg(ArgIdx)->getSourceRange(); QualType DestTy = Dest->getType(); if (const PointerType *DestPtrTy = DestTy->getAs<PointerType>()) { QualType PointeeTy = DestPtrTy->getPointeeType(); + + // Never warn about void type pointers. This can be used to suppress + // false positives. if (PointeeTy->isVoidType()) continue; + // Catch "memset(p, 0, sizeof(p))" -- needs to be sizeof(*p). Do this by + // actually comparing the expressions for equality. Because computing the + // expression IDs can be expensive, we only do this if the diagnostic is + // enabled. + if (SizeOfArg && + Diags.getDiagnosticLevel(diag::warn_sizeof_pointer_expr_memaccess, + SizeOfArg->getExprLoc())) { + // We only compute IDs for expressions if the warning is enabled, and + // cache the sizeof arg's ID. + if (SizeOfArgID == llvm::FoldingSetNodeID()) + SizeOfArg->Profile(SizeOfArgID, Context, true); + llvm::FoldingSetNodeID DestID; + Dest->Profile(DestID, Context, true); + if (DestID == SizeOfArgID) { + unsigned ActionIdx = 0; // Default is to suggest dereferencing. + if (const UnaryOperator *UnaryOp = dyn_cast<UnaryOperator>(Dest)) + if (UnaryOp->getOpcode() == UO_AddrOf) + ActionIdx = 1; // If its an address-of operator, just remove it. + if (Context.getTypeSize(PointeeTy) == Context.getCharWidth()) + ActionIdx = 2; // If the pointee's size is sizeof(char), + // suggest an explicit length. + DiagRuntimeBehavior(SizeOfArg->getExprLoc(), Dest, + PDiag(diag::warn_sizeof_pointer_expr_memaccess) + << FnName << ArgIdx << ActionIdx + << Dest->getSourceRange() + << SizeOfArg->getSourceRange()); + break; + } + } + + // Also check for cases where the sizeof argument is the exact same + // type as the memory argument, and where it points to a user-defined + // record type. + if (SizeOfArgTy != QualType()) { + if (PointeeTy->isRecordType() && + Context.typesAreCompatible(SizeOfArgTy, DestTy)) { + DiagRuntimeBehavior(LenExpr->getExprLoc(), Dest, + PDiag(diag::warn_sizeof_pointer_type_memaccess) + << FnName << SizeOfArgTy << ArgIdx + << PointeeTy << Dest->getSourceRange() + << LenExpr->getSourceRange()); + break; + } + } + + unsigned DiagID; + // Always complain about dynamic classes. - if (isDynamicClassType(PointeeTy)) { - DiagRuntimeBehavior( - Dest->getExprLoc(), Dest, - PDiag(diag::warn_dyn_class_memaccess) - << ArgIdx << FnName << PointeeTy - << Call->getCallee()->getSourceRange()); - } else { + if (isDynamicClassType(PointeeTy)) + DiagID = diag::warn_dyn_class_memaccess; + else if (PointeeTy.hasNonTrivialObjCLifetime() && CMF != CMF_Memset) + DiagID = diag::warn_arc_object_memaccess; + else continue; - } - SourceRange ArgRange = Call->getArg(0)->getSourceRange(); + DiagRuntimeBehavior( + Dest->getExprLoc(), Dest, + PDiag(DiagID) + << ArgIdx << FnName << PointeeTy + << Call->getCallee()->getSourceRange()); + DiagRuntimeBehavior( Dest->getExprLoc(), Dest, PDiag(diag::note_bad_memaccess_silence) @@ -1873,7 +1999,8 @@ Sema::CheckReturnStackAddr(Expr *RetValExp, QualType lhsType, // Perform checking for returned stack addresses, local blocks, // label addresses or references to temporaries. - if (lhsType->isPointerType() || lhsType->isBlockPointerType()) { + if (lhsType->isPointerType() || + (!getLangOptions().ObjCAutoRefCount && lhsType->isBlockPointerType())) { stackE = EvalAddr(RetValExp, refVars); } else if (lhsType->isReferenceType()) { stackE = EvalVal(RetValExp, refVars); @@ -2044,7 +2171,8 @@ static Expr *EvalAddr(Expr *E, llvm::SmallVectorImpl<DeclRefExpr *> &refVars) { // pointer values, and pointer-to-pointer conversions. case Stmt::ImplicitCastExprClass: case Stmt::CStyleCastExprClass: - case Stmt::CXXFunctionalCastExprClass: { + case Stmt::CXXFunctionalCastExprClass: + case Stmt::ObjCBridgedCastExprClass: { Expr* SubExpr = cast<CastExpr>(E)->getSubExpr(); QualType T = SubExpr->getType(); @@ -2077,6 +2205,14 @@ static Expr *EvalAddr(Expr *E, llvm::SmallVectorImpl<DeclRefExpr *> &refVars) { return NULL; } + case Stmt::MaterializeTemporaryExprClass: + if (Expr *Result = EvalAddr( + cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(), + refVars)) + return Result; + + return E; + // Everything else: we simply don't reason about them. default: return NULL; @@ -2178,6 +2314,14 @@ do { return EvalVal(M->getBase(), refVars); } + case Stmt::MaterializeTemporaryExprClass: + if (Expr *Result = EvalVal( + cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(), + refVars)) + return Result; + + return E; + default: // Check that we don't return or take the address of a reference to a // temporary. This is only useful in C++. @@ -2442,15 +2586,24 @@ IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) { case BO_NE: return IntRange::forBoolType(); - // The type of these compound assignments is the type of the LHS, - // so the RHS is not necessarily an integer. + // The type of the assignments is the type of the LHS, so the RHS + // is not necessarily the same type. case BO_MulAssign: case BO_DivAssign: case BO_RemAssign: case BO_AddAssign: case BO_SubAssign: + case BO_XorAssign: + case BO_OrAssign: + // TODO: bitfields? return IntRange::forValueOfType(C, E->getType()); + // Simple assignments just pass through the RHS, which will have + // been coerced to the LHS type. + case BO_Assign: + // TODO: bitfields? + return GetExprRange(C, BO->getRHS(), MaxWidth); + // Operations with opaque sources are black-listed. case BO_PtrMemD: case BO_PtrMemI: @@ -2506,14 +2659,54 @@ IntRange GetExprRange(ASTContext &C, Expr *E, unsigned MaxWidth) { case BO_Sub: if (BO->getLHS()->getType()->isPointerType()) return IntRange::forValueOfType(C, E->getType()); - // fallthrough + break; - default: + // The width of a division result is mostly determined by the size + // of the LHS. + case BO_Div: { + // Don't 'pre-truncate' the operands. + unsigned opWidth = C.getIntWidth(E->getType()); + IntRange L = GetExprRange(C, BO->getLHS(), opWidth); + + // If the divisor is constant, use that. + llvm::APSInt divisor; + if (BO->getRHS()->isIntegerConstantExpr(divisor, C)) { + unsigned log2 = divisor.logBase2(); // floor(log_2(divisor)) + if (log2 >= L.Width) + L.Width = (L.NonNegative ? 0 : 1); + else + L.Width = std::min(L.Width - log2, MaxWidth); + return L; + } + + // Otherwise, just use the LHS's width. + IntRange R = GetExprRange(C, BO->getRHS(), opWidth); + return IntRange(L.Width, L.NonNegative && R.NonNegative); + } + + // The result of a remainder can't be larger than the result of + // either side. + case BO_Rem: { + // Don't 'pre-truncate' the operands. + unsigned opWidth = C.getIntWidth(E->getType()); + IntRange L = GetExprRange(C, BO->getLHS(), opWidth); + IntRange R = GetExprRange(C, BO->getRHS(), opWidth); + + IntRange meet = IntRange::meet(L, R); + meet.Width = std::min(meet.Width, MaxWidth); + return meet; + } + + // The default behavior is okay for these. + case BO_Mul: + case BO_Add: + case BO_Xor: + case BO_Or: break; } - // Treat every other operator as if it were closed on the - // narrowest type that encompasses both operands. + // The default case is to treat the operation as if it were closed + // on the narrowest type that encompasses both operands. IntRange L = GetExprRange(C, BO->getLHS(), MaxWidth); IntRange R = GetExprRange(C, BO->getRHS(), MaxWidth); return IntRange::join(L, R); @@ -2837,18 +3030,16 @@ void DiagnoseFloatingLiteralImpCast(Sema &S, FloatingLiteral *FL, QualType T, if (&Value.getSemantics() == &llvm::APFloat::PPCDoubleDouble) return; - // Try to convert this exactly to an 64-bit integer. FIXME: It would be - // nice to support arbitrarily large integers here. + // Try to convert this exactly to an integer. bool isExact = false; - uint64_t IntegerPart; - if (Value.convertToInteger(&IntegerPart, 64, /*isSigned=*/true, + llvm::APSInt IntegerValue(S.Context.getIntWidth(T), + T->hasUnsignedIntegerRepresentation()); + if (Value.convertToInteger(IntegerValue, llvm::APFloat::rmTowardZero, &isExact) != llvm::APFloat::opOK || !isExact) return; - llvm::APInt IntegerValue(64, IntegerPart, /*isSigned=*/true); - - std::string LiteralValue = IntegerValue.toString(10, /*isSigned=*/true); + std::string LiteralValue = IntegerValue.toString(10); S.Diag(FL->getExprLoc(), diag::note_fix_integral_float_as_integer) << FixItHint::CreateReplacement(FL->getSourceRange(), LiteralValue); } @@ -2895,6 +3086,11 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, return; return DiagnoseImpCast(S, E, T, CC, diag::warn_impcast_vector_scalar); } + + // If the vector cast is cast between two vectors of the same size, it is + // a bitcast, not a conversion. + if (S.Context.getTypeSize(Source) == S.Context.getTypeSize(Target)) + return; Source = cast<VectorType>(Source)->getElementType().getTypePtr(); Target = cast<VectorType>(Target)->getElementType().getTypePtr(); @@ -2989,9 +3185,7 @@ void CheckImplicitConversion(Sema &S, Expr *E, QualType T, return; } - // People want to build with -Wshorten-64-to-32 and not -Wconversion - // and by god we'll let them. - + // People want to build with -Wshorten-64-to-32 and not -Wconversion. if (isFromSystemMacro(S, CC)) return; @@ -3356,3 +3550,268 @@ void Sema::CheckArrayAccess(const Expr *expr) { } } } + +//===--- CHECK: Objective-C retain cycles ----------------------------------// + +namespace { + struct RetainCycleOwner { + RetainCycleOwner() : Variable(0), Indirect(false) {} + VarDecl *Variable; + SourceRange Range; + SourceLocation Loc; + bool Indirect; + + void setLocsFrom(Expr *e) { + Loc = e->getExprLoc(); + Range = e->getSourceRange(); + } + }; +} + +/// Consider whether capturing the given variable can possibly lead to +/// a retain cycle. +static bool considerVariable(VarDecl *var, Expr *ref, RetainCycleOwner &owner) { + // In ARC, it's captured strongly iff the variable has __strong + // lifetime. In MRR, it's captured strongly if the variable is + // __block and has an appropriate type. + if (var->getType().getObjCLifetime() != Qualifiers::OCL_Strong) + return false; + + owner.Variable = var; + owner.setLocsFrom(ref); + return true; +} + +static bool findRetainCycleOwner(Expr *e, RetainCycleOwner &owner) { + while (true) { + e = e->IgnoreParens(); + if (CastExpr *cast = dyn_cast<CastExpr>(e)) { + switch (cast->getCastKind()) { + case CK_BitCast: + case CK_LValueBitCast: + case CK_LValueToRValue: + case CK_ObjCReclaimReturnedObject: + e = cast->getSubExpr(); + continue; + + case CK_GetObjCProperty: { + // Bail out if this isn't a strong explicit property. + const ObjCPropertyRefExpr *pre = cast->getSubExpr()->getObjCProperty(); + if (pre->isImplicitProperty()) return false; + ObjCPropertyDecl *property = pre->getExplicitProperty(); + if (!(property->getPropertyAttributes() & + (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_copy | + ObjCPropertyDecl::OBJC_PR_strong)) && + !(property->getPropertyIvarDecl() && + property->getPropertyIvarDecl()->getType() + .getObjCLifetime() == Qualifiers::OCL_Strong)) + return false; + + owner.Indirect = true; + e = const_cast<Expr*>(pre->getBase()); + continue; + } + + default: + return false; + } + } + + if (ObjCIvarRefExpr *ref = dyn_cast<ObjCIvarRefExpr>(e)) { + ObjCIvarDecl *ivar = ref->getDecl(); + if (ivar->getType().getObjCLifetime() != Qualifiers::OCL_Strong) + return false; + + // Try to find a retain cycle in the base. + if (!findRetainCycleOwner(ref->getBase(), owner)) + return false; + + if (ref->isFreeIvar()) owner.setLocsFrom(ref); + owner.Indirect = true; + return true; + } + + if (DeclRefExpr *ref = dyn_cast<DeclRefExpr>(e)) { + VarDecl *var = dyn_cast<VarDecl>(ref->getDecl()); + if (!var) return false; + return considerVariable(var, ref, owner); + } + + if (BlockDeclRefExpr *ref = dyn_cast<BlockDeclRefExpr>(e)) { + owner.Variable = ref->getDecl(); + owner.setLocsFrom(ref); + return true; + } + + if (MemberExpr *member = dyn_cast<MemberExpr>(e)) { + if (member->isArrow()) return false; + + // Don't count this as an indirect ownership. + e = member->getBase(); + continue; + } + + // Array ivars? + + return false; + } +} + +namespace { + struct FindCaptureVisitor : EvaluatedExprVisitor<FindCaptureVisitor> { + FindCaptureVisitor(ASTContext &Context, VarDecl *variable) + : EvaluatedExprVisitor<FindCaptureVisitor>(Context), + Variable(variable), Capturer(0) {} + + VarDecl *Variable; + Expr *Capturer; + + void VisitDeclRefExpr(DeclRefExpr *ref) { + if (ref->getDecl() == Variable && !Capturer) + Capturer = ref; + } + + void VisitBlockDeclRefExpr(BlockDeclRefExpr *ref) { + if (ref->getDecl() == Variable && !Capturer) + Capturer = ref; + } + + void VisitObjCIvarRefExpr(ObjCIvarRefExpr *ref) { + if (Capturer) return; + Visit(ref->getBase()); + if (Capturer && ref->isFreeIvar()) + Capturer = ref; + } + + void VisitBlockExpr(BlockExpr *block) { + // Look inside nested blocks + if (block->getBlockDecl()->capturesVariable(Variable)) + Visit(block->getBlockDecl()->getBody()); + } + }; +} + +/// Check whether the given argument is a block which captures a +/// variable. +static Expr *findCapturingExpr(Sema &S, Expr *e, RetainCycleOwner &owner) { + assert(owner.Variable && owner.Loc.isValid()); + + e = e->IgnoreParenCasts(); + BlockExpr *block = dyn_cast<BlockExpr>(e); + if (!block || !block->getBlockDecl()->capturesVariable(owner.Variable)) + return 0; + + FindCaptureVisitor visitor(S.Context, owner.Variable); + visitor.Visit(block->getBlockDecl()->getBody()); + return visitor.Capturer; +} + +static void diagnoseRetainCycle(Sema &S, Expr *capturer, + RetainCycleOwner &owner) { + assert(capturer); + assert(owner.Variable && owner.Loc.isValid()); + + S.Diag(capturer->getExprLoc(), diag::warn_arc_retain_cycle) + << owner.Variable << capturer->getSourceRange(); + S.Diag(owner.Loc, diag::note_arc_retain_cycle_owner) + << owner.Indirect << owner.Range; +} + +/// Check for a keyword selector that starts with the word 'add' or +/// 'set'. +static bool isSetterLikeSelector(Selector sel) { + if (sel.isUnarySelector()) return false; + + llvm::StringRef str = sel.getNameForSlot(0); + while (!str.empty() && str.front() == '_') str = str.substr(1); + if (str.startswith("set") || str.startswith("add")) + str = str.substr(3); + else + return false; + + if (str.empty()) return true; + return !islower(str.front()); +} + +/// Check a message send to see if it's likely to cause a retain cycle. +void Sema::checkRetainCycles(ObjCMessageExpr *msg) { + // Only check instance methods whose selector looks like a setter. + if (!msg->isInstanceMessage() || !isSetterLikeSelector(msg->getSelector())) + return; + + // Try to find a variable that the receiver is strongly owned by. + RetainCycleOwner owner; + if (msg->getReceiverKind() == ObjCMessageExpr::Instance) { + if (!findRetainCycleOwner(msg->getInstanceReceiver(), owner)) + return; + } else { + assert(msg->getReceiverKind() == ObjCMessageExpr::SuperInstance); + owner.Variable = getCurMethodDecl()->getSelfDecl(); + owner.Loc = msg->getSuperLoc(); + owner.Range = msg->getSuperLoc(); + } + + // Check whether the receiver is captured by any of the arguments. + for (unsigned i = 0, e = msg->getNumArgs(); i != e; ++i) + if (Expr *capturer = findCapturingExpr(*this, msg->getArg(i), owner)) + return diagnoseRetainCycle(*this, capturer, owner); +} + +/// Check a property assign to see if it's likely to cause a retain cycle. +void Sema::checkRetainCycles(Expr *receiver, Expr *argument) { + RetainCycleOwner owner; + if (!findRetainCycleOwner(receiver, owner)) + return; + + if (Expr *capturer = findCapturingExpr(*this, argument, owner)) + diagnoseRetainCycle(*this, capturer, owner); +} + +bool Sema::checkUnsafeAssigns(SourceLocation Loc, + QualType LHS, Expr *RHS) { + Qualifiers::ObjCLifetime LT = LHS.getObjCLifetime(); + if (LT != Qualifiers::OCL_Weak && LT != Qualifiers::OCL_ExplicitNone) + return false; + // strip off any implicit cast added to get to the one arc-specific + while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) { + if (cast->getCastKind() == CK_ObjCConsumeObject) { + Diag(Loc, diag::warn_arc_retained_assign) + << (LT == Qualifiers::OCL_ExplicitNone) + << RHS->getSourceRange(); + return true; + } + RHS = cast->getSubExpr(); + } + return false; +} + +void Sema::checkUnsafeExprAssigns(SourceLocation Loc, + Expr *LHS, Expr *RHS) { + QualType LHSType = LHS->getType(); + if (checkUnsafeAssigns(Loc, LHSType, RHS)) + return; + Qualifiers::ObjCLifetime LT = LHSType.getObjCLifetime(); + // FIXME. Check for other life times. + if (LT != Qualifiers::OCL_None) + return; + + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(LHS)) { + if (PRE->isImplicitProperty()) + return; + const ObjCPropertyDecl *PD = PRE->getExplicitProperty(); + if (!PD) + return; + + unsigned Attributes = PD->getPropertyAttributes(); + if (Attributes & ObjCPropertyDecl::OBJC_PR_assign) + while (ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(RHS)) { + if (cast->getCastKind() == CK_ObjCConsumeObject) { + Diag(Loc, diag::warn_arc_retained_property_assign) + << RHS->getSourceRange(); + return; + } + RHS = cast->getSubExpr(); + } + } +} diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index e328eeb0aa2b..b555c8a9aa1f 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -1676,6 +1676,34 @@ static void AddOrdinaryNameResults(Sema::ParserCompletionContext CCC, // Fall through: conditions and statements can have expressions. case Sema::PCC_ParenthesizedExpression: + if (SemaRef.getLangOptions().ObjCAutoRefCount && + CCC == Sema::PCC_ParenthesizedExpression) { + // (__bridge <type>)<expression> + Builder.AddTypedTextChunk("__bridge"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + + // (__bridge_transfer <Objective-C type>)<expression> + Builder.AddTypedTextChunk("__bridge_transfer"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("Objective-C type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + + // (__bridge_retained <CF type>)<expression> + Builder.AddTypedTextChunk("__bridge_retained"); + Builder.AddChunk(CodeCompletionString::CK_HorizontalSpace); + Builder.AddPlaceholderChunk("CF type"); + Builder.AddChunk(CodeCompletionString::CK_RightParen); + Builder.AddPlaceholderChunk("expression"); + Results.AddResult(Result(Builder.TakeString())); + } + // Fall through + case Sema::PCC_Expression: { if (SemaRef.getLangOptions().CPlusPlus) { // 'this', if we're in a non-static member function. @@ -1828,7 +1856,8 @@ static const char *GetCompletionTypeString(QualType T, CodeCompletionAllocator &Allocator) { PrintingPolicy Policy(Context.PrintingPolicy); Policy.AnonymousTagLocations = false; - + Policy.SuppressStrongLifetime = true; + if (!T.getLocalQualifiers()) { // Built-in type names are constant strings. if (const BuiltinType *BT = dyn_cast<BuiltinType>(T)) @@ -1878,9 +1907,9 @@ static void AddResultTypeChunk(ASTContext &Context, T = Context.getTypeDeclType(cast<TypeDecl>(Enumerator->getDeclContext())); else if (isa<UnresolvedUsingValueDecl>(ND)) { /* Do nothing: ignore unresolved using declarations*/ - } else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) + } else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND)) { T = Value->getType(); - else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) + } else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND)) T = Property->getType(); if (T.isNull() || Context.hasSameType(T, Context.DependentTy)) @@ -1907,6 +1936,10 @@ static void MaybeAddSentinel(ASTContext &Context, NamedDecl *FunctionOrMethod, static std::string FormatFunctionParameter(ASTContext &Context, ParmVarDecl *Param, bool SuppressName = false) { + PrintingPolicy Policy(Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + bool ObjCMethodParam = isa<ObjCMethodDecl>(Param->getDeclContext()); if (Param->getType()->isDependentType() || !Param->getType()->isBlockPointerType()) { @@ -1917,8 +1950,7 @@ static std::string FormatFunctionParameter(ASTContext &Context, if (Param->getIdentifier() && !ObjCMethodParam && !SuppressName) Result = Param->getIdentifier()->getName(); - Param->getType().getAsStringInternal(Result, - Context.PrintingPolicy); + Param->getType().getAsStringInternal(Result, Policy); if (ObjCMethodParam) { Result = "(" + Result; @@ -1968,8 +2000,7 @@ static std::string FormatFunctionParameter(ASTContext &Context, // We were unable to find a FunctionProtoTypeLoc with parameter names // for the block; just use the parameter type as a placeholder. std::string Result; - Param->getType().getUnqualifiedType(). - getAsStringInternal(Result, Context.PrintingPolicy); + Param->getType().getUnqualifiedType().getAsStringInternal(Result, Policy); if (ObjCMethodParam) { Result = "(" + Result; @@ -1986,7 +2017,7 @@ static std::string FormatFunctionParameter(ASTContext &Context, std::string Result; QualType ResultType = Block->getTypePtr()->getResultType(); if (!ResultType->isVoidType()) - ResultType.getAsStringInternal(Result, Context.PrintingPolicy); + ResultType.getAsStringInternal(Result, Policy); Result = '^' + Result; if (!BlockProto || Block->getNumArgs() == 0) { @@ -2071,6 +2102,9 @@ static void AddTemplateParameterChunks(ASTContext &Context, unsigned MaxParameters = 0, unsigned Start = 0, bool InDefaultArg = false) { + PrintingPolicy Policy(Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + typedef CodeCompletionString::Chunk Chunk; bool FirstParameter = true; @@ -2098,8 +2132,7 @@ static void AddTemplateParameterChunks(ASTContext &Context, = dyn_cast<NonTypeTemplateParmDecl>(*P)) { if (NTTP->getIdentifier()) PlaceholderStr = NTTP->getIdentifier()->getName(); - NTTP->getType().getAsStringInternal(PlaceholderStr, - Context.PrintingPolicy); + NTTP->getType().getAsStringInternal(PlaceholderStr, Policy); HasDefaultArg = NTTP->hasDefaultArgument(); } else { assert(isa<TemplateTemplateParmDecl>(*P)); @@ -2286,6 +2319,10 @@ CodeCompletionResult::CreateCodeCompletionString(Sema &S, typedef CodeCompletionString::Chunk Chunk; CodeCompletionBuilder Result(Allocator, Priority, Availability); + PrintingPolicy Policy(S.Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + if (Kind == RK_Pattern) { Pattern->Priority = Priority; Pattern->Availability = Availability; @@ -2470,7 +2507,7 @@ CodeCompletionResult::CreateCodeCompletionString(Sema &S, if ((*P)->getType()->isBlockPointerType() && !DeclaringEntity) Arg = FormatFunctionParameter(S.Context, *P, true); else { - (*P)->getType().getAsStringInternal(Arg, S.Context.PrintingPolicy); + (*P)->getType().getAsStringInternal(Arg, Policy); Arg = "(" + Arg + ")"; if (IdentifierInfo *II = (*P)->getIdentifier()) if (DeclaringEntity || AllParametersAreInformative) @@ -2519,7 +2556,10 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString( Sema &S, CodeCompletionAllocator &Allocator) const { typedef CodeCompletionString::Chunk Chunk; - + PrintingPolicy Policy(S.Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + // FIXME: Set priority, availability appropriately. CodeCompletionBuilder Result(Allocator, 1, CXAvailability_Available); FunctionDecl *FDecl = getFunction(); @@ -2545,7 +2585,7 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString( else Result.AddTextChunk( Result.getAllocator().CopyString( - Proto->getResultType().getAsString(S.Context.PrintingPolicy))); + Proto->getResultType().getAsString(Policy))); Result.AddChunk(Chunk(CodeCompletionString::CK_LeftParen)); unsigned NumParams = FDecl? FDecl->getNumParams() : Proto->getNumArgs(); @@ -2563,7 +2603,7 @@ CodeCompleteConsumer::OverloadCandidate::CreateSignatureString( ArgType = Proto->getArgType(I); } - ArgType.getAsStringInternal(ArgString, S.Context.PrintingPolicy); + ArgType.getAsStringInternal(ArgString, Policy); if (I == CurrentArg) Result.AddChunk(Chunk(CodeCompletionString::CK_CurrentParameter, @@ -3204,8 +3244,23 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE, return; } + enum CodeCompletionContext::Kind contextKind; + + if (IsArrow) { + contextKind = CodeCompletionContext::CCC_ArrowMemberAccess; + } + else { + if (BaseType->isObjCObjectPointerType() || + BaseType->isObjCObjectOrInterfaceType()) { + contextKind = CodeCompletionContext::CCC_ObjCPropertyAccess; + } + else { + contextKind = CodeCompletionContext::CCC_DotMemberAccess; + } + } + ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompletionContext(CodeCompletionContext::CCC_MemberAccess, + CodeCompletionContext(contextKind, BaseType), &ResultBuilder::IsMember); Results.EnterNewScope(); @@ -3431,10 +3486,17 @@ void Sema::CodeCompleteCase(Scope *S) { } Results.ExitScope(); - if (CodeCompleter->includeMacros()) + //We need to make sure we're setting the right context, + //so only say we include macros if the code completer says we do + enum CodeCompletionContext::Kind kind = CodeCompletionContext::CCC_Other; + if (CodeCompleter->includeMacros()) { AddMacroResults(PP, Results); + kind = CodeCompletionContext::CCC_OtherWithMacros; + } + + HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_OtherWithMacros, + kind, Results.data(),Results.size()); } @@ -3786,6 +3848,10 @@ void Sema::CodeCompleteOperatorName(Scope *S) { void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, CXXCtorInitializer** Initializers, unsigned NumInitializers) { + PrintingPolicy Policy(Context.PrintingPolicy); + Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; + CXXConstructorDecl *Constructor = static_cast<CXXConstructorDecl *>(ConstructorD); if (!Constructor) @@ -3825,7 +3891,7 @@ void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, Builder.AddTypedTextChunk( Results.getAllocator().CopyString( - Base->getType().getAsString(Context.PrintingPolicy))); + Base->getType().getAsString(Policy))); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("args"); Builder.AddChunk(CodeCompletionString::CK_RightParen); @@ -3850,7 +3916,7 @@ void Sema::CodeCompleteConstructorInitializer(Decl *ConstructorD, Builder.AddTypedTextChunk( Builder.getAllocator().CopyString( - Base->getType().getAsString(Context.PrintingPolicy))); + Base->getType().getAsString(Policy))); Builder.AddChunk(CodeCompletionString::CK_LeftParen); Builder.AddPlaceholderChunk("args"); Builder.AddChunk(CodeCompletionString::CK_RightParen); @@ -4126,18 +4192,24 @@ static bool ObjCPropertyFlagConflicts(unsigned Attributes, unsigned NewFlag) { if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain))) + ObjCDeclSpec::DQ_PR_retain | + ObjCDeclSpec::DQ_PR_strong))) return true; - // Check for more than one of { assign, copy, retain }. + // Check for more than one of { assign, copy, retain, strong }. unsigned AssignCopyRetMask = Attributes & (ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain); + ObjCDeclSpec::DQ_PR_retain| + ObjCDeclSpec::DQ_PR_strong); if (AssignCopyRetMask && AssignCopyRetMask != ObjCDeclSpec::DQ_PR_assign && + AssignCopyRetMask != ObjCDeclSpec::DQ_PR_unsafe_unretained && AssignCopyRetMask != ObjCDeclSpec::DQ_PR_copy && - AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain) + AssignCopyRetMask != ObjCDeclSpec::DQ_PR_retain && + AssignCopyRetMask != ObjCDeclSpec::DQ_PR_strong) return true; return false; @@ -4157,10 +4229,15 @@ void Sema::CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS) { Results.AddResult(CodeCompletionResult("readonly")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_assign)) Results.AddResult(CodeCompletionResult("assign")); + if (!ObjCPropertyFlagConflicts(Attributes, + ObjCDeclSpec::DQ_PR_unsafe_unretained)) + Results.AddResult(CodeCompletionResult("unsafe_unretained")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_readwrite)) Results.AddResult(CodeCompletionResult("readwrite")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_retain)) Results.AddResult(CodeCompletionResult("retain")); + if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_strong)) + Results.AddResult(CodeCompletionResult("strong")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_copy)) Results.AddResult(CodeCompletionResult("copy")); if (!ObjCPropertyFlagConflicts(Attributes, ObjCDeclSpec::DQ_PR_nonatomic)) @@ -4522,6 +4599,7 @@ static ObjCInterfaceDecl *GetAssumedMessageSendExprType(Expr *E) { if (Method->isInstanceMethod()) return llvm::StringSwitch<ObjCInterfaceDecl *>(Id->getName()) .Case("retain", IFace) + .Case("strong", IFace) .Case("autorelease", IFace) .Case("copy", IFace) .Case("copyWithZone", IFace) @@ -4878,7 +4956,7 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, bool AtArgumentExpression, bool IsSuper) { ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompletionContext::CCC_Other); + CodeCompletionContext::CCC_ObjCClassMessage); AddClassMessageCompletions(*this, S, Receiver, SelIdents, NumSelIdents, AtArgumentExpression, IsSuper, Results); @@ -4898,7 +4976,7 @@ void Sema::CodeCompleteObjCClassMessage(Scope *S, ParsedType Receiver, } HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_Other, + CodeCompletionContext::CCC_ObjCClassMessage, Results.data(), Results.size()); } @@ -4941,7 +5019,7 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, // Build the set of methods we can see. ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompletionContext::CCC_Other); + CodeCompletionContext::CCC_ObjCInstanceMessage); Results.EnterNewScope(); // If this is a send-to-super, try to add the special "super" send @@ -5054,7 +5132,7 @@ void Sema::CodeCompleteObjCInstanceMessage(Scope *S, ExprTy *Receiver, } HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_Other, + CodeCompletionContext::CCC_ObjCInstanceMessage, Results.data(),Results.size()); } @@ -5245,8 +5323,7 @@ void Sema::CodeCompleteObjCInterfaceDecl(Scope *S) { false, Results); Results.ExitScope(); - // FIXME: Add a special context for this, use cached global completion - // results. + // FIXME: Use cached global completion results. HandleCodeCompleteResults(this, CodeCompleter, CodeCompletionContext::CCC_Other, Results.data(),Results.size()); @@ -5255,7 +5332,7 @@ void Sema::CodeCompleteObjCInterfaceDecl(Scope *S) { void Sema::CodeCompleteObjCSuperclass(Scope *S, IdentifierInfo *ClassName, SourceLocation ClassNameLoc) { ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompletionContext::CCC_Other); + CodeCompletionContext::CCC_ObjCSuperclass); Results.EnterNewScope(); // Make sure that we ignore the class we're currently defining. @@ -5269,10 +5346,9 @@ void Sema::CodeCompleteObjCSuperclass(Scope *S, IdentifierInfo *ClassName, false, Results); Results.ExitScope(); - // FIXME: Add a special context for this, use cached global completion - // results. + // FIXME: Use cached global completion results. HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_Other, + CodeCompletionContext::CCC_ObjCSuperclass, Results.data(),Results.size()); } @@ -5286,8 +5362,7 @@ void Sema::CodeCompleteObjCImplementationDecl(Scope *S) { true, Results); Results.ExitScope(); - // FIXME: Add a special context for this, use cached global completion - // results. + // FIXME: Use cached global completion results. HandleCodeCompleteResults(this, CodeCompleter, CodeCompletionContext::CCC_Other, Results.data(),Results.size()); @@ -5299,7 +5374,7 @@ void Sema::CodeCompleteObjCInterfaceCategory(Scope *S, typedef CodeCompletionResult Result; ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompletionContext::CCC_Other); + CodeCompletionContext::CCC_ObjCCategoryName); // Ignore any categories we find that have already been implemented by this // interface. @@ -5323,7 +5398,7 @@ void Sema::CodeCompleteObjCInterfaceCategory(Scope *S, Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_Other, + CodeCompletionContext::CCC_ObjCCategoryName, Results.data(),Results.size()); } @@ -5342,7 +5417,7 @@ void Sema::CodeCompleteObjCImplementationCategory(Scope *S, return CodeCompleteObjCInterfaceCategory(S, ClassName, ClassNameLoc); ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompletionContext::CCC_Other); + CodeCompletionContext::CCC_ObjCCategoryName); // Add all of the categories that have have corresponding interface // declarations in this class and any of its superclasses, except for @@ -5363,7 +5438,7 @@ void Sema::CodeCompleteObjCImplementationCategory(Scope *S, Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, - CodeCompletionContext::CCC_Other, + CodeCompletionContext::CCC_ObjCCategoryName, Results.data(),Results.size()); } @@ -6273,6 +6348,7 @@ void Sema::CodeCompleteObjCMethodDecl(Scope *S, Results.EnterNewScope(); PrintingPolicy Policy(Context.PrintingPolicy); Policy.AnonymousTagLocations = false; + Policy.SuppressStrongLifetime = true; for (KnownMethodsMap::iterator M = KnownMethods.begin(), MEnd = KnownMethods.end(); M != MEnd; ++M) { diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 9446c0e8c03f..9d91a48bdcd7 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -45,7 +45,12 @@ using namespace clang; using namespace sema; -Sema::DeclGroupPtrTy Sema::ConvertDeclToDeclGroup(Decl *Ptr) { +Sema::DeclGroupPtrTy Sema::ConvertDeclToDeclGroup(Decl *Ptr, Decl *OwnedType) { + if (OwnedType) { + Decl *Group[2] = { OwnedType, Ptr }; + return DeclGroupPtrTy::make(DeclGroupRef::Create(Context, Group, 2)); + } + return DeclGroupPtrTy::make(DeclGroupRef(Ptr)); } @@ -287,41 +292,42 @@ bool Sema::DiagnoseUnknownTypeName(const IdentifierInfo &II, // There may have been a typo in the name of the type. Look up typo // results, in case we have something that we can suggest. - LookupResult Lookup(*this, &II, IILoc, LookupOrdinaryName, - NotForRedeclaration); + if (TypoCorrection Corrected = CorrectTypo(DeclarationNameInfo(&II, IILoc), + LookupOrdinaryName, S, SS, NULL, + false, CTC_Type)) { + std::string CorrectedStr(Corrected.getAsString(getLangOptions())); + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions())); - if (DeclarationName Corrected = CorrectTypo(Lookup, S, SS, 0, 0, CTC_Type)) { - if (NamedDecl *Result = Lookup.getAsSingle<NamedDecl>()) { + if (Corrected.isKeyword()) { + // We corrected to a keyword. + // FIXME: Actually recover with the keyword we suggest, and emit a fix-it. + Diag(IILoc, diag::err_unknown_typename_suggest) + << &II << CorrectedQuotedStr; + return true; + } else { + NamedDecl *Result = Corrected.getCorrectionDecl(); if ((isa<TypeDecl>(Result) || isa<ObjCInterfaceDecl>(Result)) && !Result->isInvalidDecl()) { // We found a similarly-named type or interface; suggest that. if (!SS || !SS->isSet()) Diag(IILoc, diag::err_unknown_typename_suggest) - << &II << Lookup.getLookupName() - << FixItHint::CreateReplacement(SourceRange(IILoc), - Result->getNameAsString()); + << &II << CorrectedQuotedStr + << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr); else if (DeclContext *DC = computeDeclContext(*SS, false)) Diag(IILoc, diag::err_unknown_nested_typename_suggest) - << &II << DC << Lookup.getLookupName() << SS->getRange() - << FixItHint::CreateReplacement(SourceRange(IILoc), - Result->getNameAsString()); + << &II << DC << CorrectedQuotedStr << SS->getRange() + << FixItHint::CreateReplacement(SourceRange(IILoc), CorrectedStr); else llvm_unreachable("could not have corrected a typo here"); Diag(Result->getLocation(), diag::note_previous_decl) - << Result->getDeclName(); + << CorrectedQuotedStr; SuggestedType = getTypeName(*Result->getIdentifier(), IILoc, S, SS, false, false, ParsedType(), /*NonTrivialTypeSourceInfo=*/true); return true; } - } else if (Lookup.empty()) { - // We corrected to a keyword. - // FIXME: Actually recover with the keyword we suggest, and emit a fix-it. - Diag(IILoc, diag::err_unknown_typename_suggest) - << &II << Corrected; - return true; } } @@ -509,11 +515,15 @@ Corrected: // Perform typo correction to determine if there is another name that is // close to this name. if (!SecondTry) { - if (DeclarationName Corrected = CorrectTypo(Result, S, &SS)) { + SecondTry = true; + if (TypoCorrection Corrected = CorrectTypo(Result.getLookupNameInfo(), + Result.getLookupKind(), S, &SS)) { unsigned UnqualifiedDiag = diag::err_undeclared_var_use_suggest; unsigned QualifiedDiag = diag::err_no_member_suggest; + std::string CorrectedStr(Corrected.getAsString(getLangOptions())); + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions())); - NamedDecl *FirstDecl = Result.empty()? 0 : *Result.begin(); + NamedDecl *FirstDecl = Corrected.getCorrectionDecl(); NamedDecl *UnderlyingFirstDecl = FirstDecl? FirstDecl->getUnderlyingDecl() : 0; if (getLangOptions().CPlusPlus && NextToken.is(tok::less) && @@ -527,26 +537,33 @@ Corrected: UnqualifiedDiag = diag::err_unknown_typename_suggest; QualifiedDiag = diag::err_unknown_nested_typename_suggest; } - + if (SS.isEmpty()) Diag(NameLoc, UnqualifiedDiag) - << Name << Corrected - << FixItHint::CreateReplacement(NameLoc, Corrected.getAsString()); + << Name << CorrectedQuotedStr + << FixItHint::CreateReplacement(NameLoc, CorrectedStr); else Diag(NameLoc, QualifiedDiag) - << Name << computeDeclContext(SS, false) << Corrected + << Name << computeDeclContext(SS, false) << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(NameLoc, Corrected.getAsString()); + << FixItHint::CreateReplacement(NameLoc, CorrectedStr); // Update the name, so that the caller has the new name. - Name = Corrected.getAsIdentifierInfo(); + Name = Corrected.getCorrectionAsIdentifierInfo(); + // Also update the LookupResult... + // FIXME: This should probably go away at some point + Result.clear(); + Result.setLookupName(Corrected.getCorrection()); + if (FirstDecl) Result.addDecl(FirstDecl); + // Typo correction corrected to a keyword. - if (Result.empty()) - return Corrected.getAsIdentifierInfo(); + if (Corrected.isKeyword()) + return Corrected.getCorrectionAsIdentifierInfo(); - Diag(FirstDecl->getLocation(), diag::note_previous_decl) - << FirstDecl->getDeclName(); + if (FirstDecl) + Diag(FirstDecl->getLocation(), diag::note_previous_decl) + << CorrectedQuotedStr; // If we found an Objective-C instance variable, let // LookupInObjCMethod build the appropriate expression to @@ -1137,17 +1154,18 @@ void Sema::ActOnPopScope(SourceLocation Loc, Scope *S) { /// class could not be found. ObjCInterfaceDecl *Sema::getObjCInterfaceDecl(IdentifierInfo *&Id, SourceLocation IdLoc, - bool TypoCorrection) { + bool DoTypoCorrection) { // The third "scope" argument is 0 since we aren't enabling lazy built-in // creation from this context. NamedDecl *IDecl = LookupSingleName(TUScope, Id, IdLoc, LookupOrdinaryName); - if (!IDecl && TypoCorrection) { + if (!IDecl && DoTypoCorrection) { // Perform typo correction at the given location, but only if we // find an Objective-C class name. - LookupResult R(*this, Id, IdLoc, LookupOrdinaryName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (IDecl = R.getAsSingle<ObjCInterfaceDecl>())) { + TypoCorrection C; + if ((C = CorrectTypo(DeclarationNameInfo(Id, IdLoc), LookupOrdinaryName, + TUScope, NULL, NULL, false, CTC_NoKeywords)) && + (IDecl = C.getCorrectionDeclAs<ObjCInterfaceDecl>())) { Diag(IdLoc, diag::err_undef_interface_suggest) << Id << IDecl->getDeclName() << FixItHint::CreateReplacement(IdLoc, IDecl->getNameAsString()); @@ -1359,7 +1377,7 @@ void Sema::MergeTypedefNameDecl(TypedefNameDecl *New, LookupResult &OldDecls) { // The types match. Link up the redeclaration chain if the old // declaration was a typedef. - // FIXME: this is a potential source of wierdness if the type + // FIXME: this is a potential source of weirdness if the type // spellings don't match exactly. if (TypedefNameDecl *Typedef = dyn_cast<TypedefNameDecl>(Old)) New->setPreviousDeclaration(Typedef); @@ -1527,7 +1545,8 @@ Sema::CXXSpecialMember Sema::getSpecialMember(const CXXMethodDecl *MD) { /// GNU89 mode. static bool canRedefineFunction(const FunctionDecl *FD, const LangOptions& LangOpts) { - return (LangOpts.GNUInline && !LangOpts.CPlusPlus && + return ((FD->hasAttr<GNUInlineAttr>() || LangOpts.GNUInline) && + !LangOpts.CPlusPlus && FD->isInlineSpecified() && FD->getStorageClass() == SC_Extern); } @@ -1927,6 +1946,7 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old) { return false; } + void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, const ObjCMethodDecl *oldMethod) { // Merge the attributes. @@ -2037,6 +2057,15 @@ void Sema::MergeVarDecl(VarDecl *New, LookupResult &Previous) { } mergeDeclAttributes(New, Old, Context); + // Warn if an already-declared variable is made a weak_import in a subsequent declaration + if (New->getAttr<WeakImportAttr>() && + Old->getStorageClass() == SC_None && + !Old->getAttr<WeakImportAttr>()) { + Diag(New->getLocation(), diag::warn_weak_import) << New->getDeclName(); + Diag(Old->getLocation(), diag::note_previous_definition); + // Remove weak_import attribute on new declaration. + New->dropAttr<WeakImportAttr>(); + } // Merge the types. MergeVarDeclTypes(New, Old); @@ -2597,7 +2626,7 @@ Decl *Sema::BuildAnonymousStructOrUnion(Scope *S, DeclSpec &DS, } // Mock up a declarator. - Declarator Dc(DS, Declarator::TypeNameContext); + Declarator Dc(DS, Declarator::MemberContext); TypeSourceInfo *TInfo = GetTypeForDeclarator(Dc, S); assert(TInfo && "couldn't build declarator info for anonymous struct/union"); @@ -2736,6 +2765,7 @@ Sema::GetNameFromUnqualifiedId(const UnqualifiedId &Name) { switch (Name.getKind()) { + case UnqualifiedId::IK_ImplicitSelfParam: case UnqualifiedId::IK_Identifier: NameInfo.setName(Name.Identifier); NameInfo.setLoc(Name.StartLocation); @@ -2924,10 +2954,9 @@ static bool RebuildDeclaratorInCurrentInstantiation(Sema &S, Declarator &D, return false; } -Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D, - bool IsFunctionDefinition) { +Decl *Sema::ActOnDeclarator(Scope *S, Declarator &D) { return HandleDeclarator(S, D, MultiTemplateParamsArg(*this), - IsFunctionDefinition); + /*IsFunctionDefinition=*/false); } /// DiagnoseClassNameShadow - Implement C++ [class.mem]p13: @@ -3274,8 +3303,18 @@ Sema::RegisterLocallyScopedExternCDecl(NamedDecl *ND, if (S && IdResolver.ReplaceDecl(PrevDecl, ND)) { // The previous declaration was found on the identifer resolver // chain, so remove it from its scope. - while (S && !S->isDeclScope(PrevDecl)) - S = S->getParent(); + + if (S->isDeclScope(PrevDecl)) { + // Special case for redeclarations in the SAME scope. + // Because this declaration is going to be added to the identifier chain + // later, we should temporarily take it OFF the chain. + IdResolver.RemoveDecl(ND); + + } else { + // Find the scope for the original declaration. + while (S && !S->isDeclScope(PrevDecl)) + S = S->getParent(); + } if (S) S->RemoveDecl(PrevDecl); @@ -3472,6 +3511,50 @@ static void SetNestedNameSpecifier(DeclaratorDecl *DD, Declarator &D) { DD->setQualifierInfo(SS.getWithLocInContext(DD->getASTContext())); } +bool Sema::inferObjCARCLifetime(ValueDecl *decl) { + QualType type = decl->getType(); + Qualifiers::ObjCLifetime lifetime = type.getObjCLifetime(); + if (lifetime == Qualifiers::OCL_Autoreleasing) { + // Various kinds of declaration aren't allowed to be __autoreleasing. + unsigned kind = -1U; + if (VarDecl *var = dyn_cast<VarDecl>(decl)) { + if (var->hasAttr<BlocksAttr>()) + kind = 0; // __block + else if (!var->hasLocalStorage()) + kind = 1; // global + } else if (isa<ObjCIvarDecl>(decl)) { + kind = 3; // ivar + } else if (isa<FieldDecl>(decl)) { + kind = 2; // field + } + + if (kind != -1U) { + Diag(decl->getLocation(), diag::err_arc_autoreleasing_var) + << kind; + } + } else if (lifetime == Qualifiers::OCL_None) { + // Try to infer lifetime. + if (!type->isObjCLifetimeType()) + return false; + + lifetime = type->getObjCARCImplicitLifetime(); + type = Context.getLifetimeQualifiedType(type, lifetime); + decl->setType(type); + } + + if (VarDecl *var = dyn_cast<VarDecl>(decl)) { + // Thread-local variables cannot have lifetime. + if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone && + var->isThreadSpecified()) { + Diag(var->getLocation(), diag::err_arc_thread_ownership) + << var->getType(); + return true; + } + } + + return false; +} + NamedDecl* Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, QualType R, TypeSourceInfo *TInfo, @@ -3630,6 +3713,11 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); + // In auto-retain/release, infer strong retension for variables of + // retainable type. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewVD)) + NewVD->setInvalidDecl(); + // Handle GNU asm-label extension (encoded as an attribute). if (Expr *E = (Expr*)D.getAsmLabel()) { // The parser guarantees this is a string. @@ -4007,10 +4095,10 @@ bool Sema::AddOverriddenMethods(CXXRecordDecl *DC, CXXMethodDecl *MD) { for (CXXBasePaths::decl_iterator I = Paths.found_decls_begin(), E = Paths.found_decls_end(); I != E; ++I) { if (CXXMethodDecl *OldMD = dyn_cast<CXXMethodDecl>(*I)) { + MD->addOverriddenMethod(OldMD->getCanonicalDecl()); if (!CheckOverridingFunctionReturnType(MD, OldMD) && !CheckOverridingFunctionExceptionSpec(MD, OldMD) && !CheckIfOverriddenFunctionIsMarkedFinal(MD, OldMD)) { - MD->addOverriddenMethod(OldMD->getCanonicalDecl()); AddedAny = true; } } @@ -4035,7 +4123,7 @@ static void DiagnoseInvalidRedeclaration(Sema &S, FunctionDecl *NewFD) { } NamedDecl* -Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, +Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC, QualType R, TypeSourceInfo *TInfo, LookupResult &Previous, MultiTemplateParamsArg TemplateParamLists, @@ -4611,9 +4699,18 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // A storage-class-specifier shall not be specified in an explicit // specialization (14.7.3) if (SC != SC_None) { - Diag(NewFD->getLocation(), - diag::err_explicit_specialization_storage_class) - << FixItHint::CreateRemoval(D.getDeclSpec().getStorageClassSpecLoc()); + if (SC != NewFD->getStorageClass()) + Diag(NewFD->getLocation(), + diag::err_explicit_specialization_inconsistent_storage_class) + << SC + << FixItHint::CreateRemoval( + D.getDeclSpec().getStorageClassSpecLoc()); + + else + Diag(NewFD->getLocation(), + diag::ext_explicit_specialization_storage_class) + << FixItHint::CreateRemoval( + D.getDeclSpec().getStorageClassSpecLoc()); } } else if (isExplicitSpecialization && isa<CXXMethodDecl>(NewFD)) { @@ -5208,12 +5305,8 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, VarDecl *VDecl = dyn_cast<VarDecl>(RealDecl); if (!VDecl) { - if (getLangOptions().CPlusPlus && - RealDecl->getLexicalDeclContext()->isRecord() && - isa<NamedDecl>(RealDecl)) - Diag(RealDecl->getLocation(), diag::err_member_initialization); - else - Diag(RealDecl->getLocation(), diag::err_illegal_initializer); + assert(!isa<FieldDecl>(RealDecl) && "field init shouldn't get here"); + Diag(RealDecl->getLocation(), diag::err_illegal_initializer); RealDecl->setInvalidDecl(); return; } @@ -5232,6 +5325,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, VDecl->setTypeSourceInfo(DeducedType); VDecl->setType(DeducedType->getType()); + // In ARC, infer lifetime. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) + VDecl->setInvalidDecl(); + // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. if (VarDecl *Old = VDecl->getPreviousDeclaration()) @@ -5372,15 +5469,23 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // We allow integer constant expressions in all cases. } else if (T->isIntegralOrEnumerationType()) { - if (!Init->isValueDependent()) { - // Check whether the expression is a constant expression. - llvm::APSInt Value; - SourceLocation Loc; - if (!Init->isIntegerConstantExpr(Value, Context, &Loc)) { - Diag(Loc, diag::err_in_class_initializer_non_constant) - << Init->getSourceRange(); - VDecl->setInvalidDecl(); - } + // Check whether the expression is a constant expression. + SourceLocation Loc; + if (Init->isValueDependent()) + ; // Nothing to check. + else if (Init->isIntegerConstantExpr(Context, &Loc)) + ; // Ok, it's an ICE! + else if (Init->isEvaluatable(Context)) { + // If we can constant fold the initializer through heroics, accept it, + // but report this as a use of an extension for -pedantic. + Diag(Loc, diag::ext_in_class_initializer_non_constant) + << Init->getSourceRange(); + } else { + // Otherwise, this is some crazy unknown case. Report the issue at the + // location provided by the isIntegerConstantExpr failed check. + Diag(Loc, diag::err_in_class_initializer_non_constant) + << Init->getSourceRange(); + VDecl->setInvalidDecl(); } // We allow floating-point constants as an extension in C++03, and @@ -5466,7 +5571,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, // Check any implicit conversions within the expression. CheckImplicitConversions(Init, VDecl->getLocation()); - + + if (!VDecl->isInvalidDecl()) + checkUnsafeAssigns(VDecl->getLocation(), VDecl->getType(), Init); + Init = MaybeCreateExprWithCleanups(Init); // Attach the initializer to the decl. VDecl->setInit(Init); @@ -5735,6 +5843,23 @@ void Sema::ActOnCXXForRangeDecl(Decl *D) { void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (var->isInvalidDecl()) return; + // In ARC, don't allow jumps past the implicit initialization of a + // local retaining variable. + if (getLangOptions().ObjCAutoRefCount && + var->hasLocalStorage()) { + switch (var->getType().getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + break; + + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + getCurFunction()->setHasBranchProtectedScope(); + break; + } + } + // All the following checks are C++ only. if (!getLangOptions().CPlusPlus) return; @@ -5874,21 +5999,13 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) { DiagnoseFunctionSpecifiers(D); - TagDecl *OwnedDecl = 0; - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S, &OwnedDecl); + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType parmDeclType = TInfo->getType(); if (getLangOptions().CPlusPlus) { // Check that there are no default arguments inside the type of this // parameter. CheckExtraCXXDefaultArguments(D); - - if (OwnedDecl && OwnedDecl->isDefinition()) { - // C++ [dcl.fct]p6: - // Types shall not be defined in return or parameter types. - Diag(OwnedDecl->getLocation(), diag::err_type_defined_in_param_type) - << Context.getTypeDeclType(OwnedDecl); - } // Parameter declarators cannot be qualified (C++ [dcl.meaning]p1). if (D.getCXXScopeSpec().isSet()) { @@ -6003,7 +6120,7 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue(ParmVarDecl * const *Param, // Warn if the return value is pass-by-value and larger than the specified // threshold. - if (ReturnTy->isPODType()) { + if (ReturnTy.isPODType(Context)) { unsigned Size = Context.getTypeSizeInChars(ReturnTy).getQuantity(); if (Size > LangOpts.NumLargeByValueCopy) Diag(D->getLocation(), diag::warn_return_value_size) @@ -6014,7 +6131,7 @@ void Sema::DiagnoseSizeOfParametersAndReturnValue(ParmVarDecl * const *Param, // threshold. for (; Param != ParamEnd; ++Param) { QualType T = (*Param)->getType(); - if (!T->isPODType()) + if (!T.isPODType(Context)) continue; unsigned Size = Context.getTypeSizeInChars(T).getQuantity(); if (Size > LangOpts.NumLargeByValueCopy) @@ -6028,8 +6145,31 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, QualType T, TypeSourceInfo *TSInfo, VarDecl::StorageClass StorageClass, VarDecl::StorageClass StorageClassAsWritten) { + // In ARC, infer a lifetime qualifier for appropriate parameter types. + if (getLangOptions().ObjCAutoRefCount && + T.getObjCLifetime() == Qualifiers::OCL_None && + T->isObjCLifetimeType()) { + + Qualifiers::ObjCLifetime lifetime; + + // Special cases for arrays: + // - if it's const, use __unsafe_unretained + // - otherwise, it's an error + if (T->isArrayType()) { + if (!T.isConstQualified()) { + Diag(NameLoc, diag::err_arc_array_param_no_ownership) + << TSInfo->getTypeLoc().getSourceRange(); + } + lifetime = Qualifiers::OCL_ExplicitNone; + } else { + lifetime = T->getObjCARCImplicitLifetime(); + } + T = Context.getLifetimeQualifiedType(T, lifetime); + } + ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name, - adjustParameterType(T), TSInfo, + Context.getAdjustedParameterType(T), + TSInfo, StorageClass, StorageClassAsWritten, 0); @@ -6318,6 +6458,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // Implements C++ [basic.start.main]p5 and C99 5.1.2.2.3. FD->setHasImplicitReturnZero(true); WP.disableCheckFallThrough(); + } else if (FD->hasAttr<NakedAttr>()) { + // If the function is marked 'naked', don't complain about missing return + // statements. + WP.disableCheckFallThrough(); } // MSVC permits the use of pure specifier (=0) on function definition, @@ -6364,7 +6508,7 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // Verify that that gotos and switch cases don't jump into scopes illegally. if (getCurFunction()->NeedsScopeChecking() && !dcl->isInvalidDecl() && - !hasAnyErrorsInThisFunction()) + !hasAnyUnrecoverableErrorsInThisFunction()) DiagnoseInvalidJumps(Body); if (CXXDestructorDecl *Destructor = dyn_cast<CXXDestructorDecl>(dcl)) { @@ -6379,15 +6523,17 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. if (PP.getDiagnostics().hasErrorOccurred() || - PP.getDiagnostics().getSuppressAllDiagnostics()) + PP.getDiagnostics().getSuppressAllDiagnostics()) { ExprTemporaries.clear(); - else if (!isa<FunctionTemplateDecl>(dcl)) { + ExprNeedsCleanups = false; + } else if (!isa<FunctionTemplateDecl>(dcl)) { // Since the body is valid, issue any analysis-based warnings that are // enabled. ActivePolicy = &WP; } assert(ExprTemporaries.empty() && "Leftover temporaries in function"); + assert(!ExprNeedsCleanups && "Unaccounted cleanups in function"); } if (!IsInstantiation) @@ -6398,8 +6544,10 @@ Decl *Sema::ActOnFinishFunctionBody(Decl *dcl, Stmt *Body, // If any errors have occurred, clear out any temporaries that may have // been leftover. This ensures that these temporaries won't be picked up for // deletion in some later function. - if (getDiagnostics().hasErrorOccurred()) + if (getDiagnostics().hasErrorOccurred()) { ExprTemporaries.clear(); + ExprNeedsCleanups = false; + } return dcl; } @@ -6439,6 +6587,7 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, Declarator D(DS, Declarator::BlockContext); D.AddTypeInfo(DeclaratorChunk::getFunction(false, false, SourceLocation(), 0, 0, 0, true, SourceLocation(), + SourceLocation(), EST_None, SourceLocation(), 0, 0, 0, 0, Loc, Loc, D), DS.getAttributes(), @@ -6466,6 +6615,9 @@ NamedDecl *Sema::ImplicitlyDefineFunction(SourceLocation Loc, /// These attributes can apply both to implicitly-declared builtins /// (like __builtin___printf_chk) or to library-declared functions /// like NSLog or printf. +/// +/// We need to check for duplicate attributes both here and where user-written +/// attributes are applied to declarations. void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) { if (FD->isInvalidDecl()) return; @@ -6499,9 +6651,9 @@ void Sema::AddKnownFunctionAttributes(FunctionDecl *FD) { FD->addAttr(::new (Context) ConstAttr(FD->getLocation(), Context)); } - if (Context.BuiltinInfo.isNoThrow(BuiltinID)) + if (Context.BuiltinInfo.isNoThrow(BuiltinID) && !FD->getAttr<NoThrowAttr>()) FD->addAttr(::new (Context) NoThrowAttr(FD->getLocation(), Context)); - if (Context.BuiltinInfo.isConst(BuiltinID)) + if (Context.BuiltinInfo.isConst(BuiltinID) && !FD->getAttr<ConstAttr>()) FD->addAttr(::new (Context) ConstAttr(FD->getLocation(), Context)); } @@ -7728,6 +7880,11 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, // FIXME: What to pass instead of TUScope? ProcessDeclAttributes(TUScope, NewFD, *D); + // In auto-retain/release, infer strong retension for fields of + // retainable type. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewFD)) + NewFD->setInvalidDecl(); + if (T.isObjCGCWeak()) Diag(Loc, diag::warn_attribute_weak_on_field); @@ -7761,6 +7918,21 @@ bool Sema::CheckNontrivialField(FieldDecl *FD) { member = CXXDestructor; if (member != CXXInvalid) { + if (getLangOptions().ObjCAutoRefCount && RDecl->hasObjectMember()) { + // Objective-C++ ARC: it is an error to have a non-trivial field of + // a union. However, system headers in Objective-C programs + // occasionally have Objective-C lifetime objects within unions, + // and rather than cause the program to fail, we make those + // members unavailable. + SourceLocation Loc = FD->getLocation(); + if (getSourceManager().isInSystemHeader(Loc)) { + if (!FD->hasAttr<UnavailableAttr>()) + FD->addAttr(new (Context) UnavailableAttr(Loc, Context, + "this system field has retaining ownership")); + return false; + } + } + Diag(FD->getLocation(), diag::err_illegal_union_or_anon_struct_member) << (int)FD->getParent()->isUnion() << FD->getDeclName() << member; DiagnoseNontrivial(RT, member); @@ -7913,6 +8085,21 @@ void Sema::DiagnoseNontrivial(const RecordType* T, CXXSpecialMember member) { return; } } + + if (EltTy->isObjCLifetimeType()) { + switch (EltTy.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + break; + + case Qualifiers::OCL_Autoreleasing: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + Diag((*fi)->getLocation(), diag::note_nontrivial_objc_ownership) + << QT << EltTy.getObjCLifetime(); + return; + } + } } assert(0 && "found no explanation for non-trivial member"); @@ -8022,6 +8209,10 @@ Decl *Sema::ActOnIvar(Scope *S, if (D.isInvalidType()) NewID->setInvalidDecl(); + // In ARC, infer 'retaining' for ivars of retainable type. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(NewID)) + NewID->setInvalidDecl(); + if (II) { // FIXME: When interfaces are DeclContexts, we'll need to add // these to the interface. @@ -8093,6 +8284,7 @@ void Sema::ActOnFields(Scope* S, llvm::SmallVector<FieldDecl*, 32> RecFields; RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl); + bool ARCErrReported = false; for (unsigned i = 0; i != NumFields; ++i) { FieldDecl *FD = cast<FieldDecl>(Fields[i]); @@ -8158,7 +8350,7 @@ void Sema::ActOnFields(Scope* S, continue; } if (!FD->getType()->isDependentType() && - !Context.getBaseElementType(FD->getType())->isPODType()) { + !Context.getBaseElementType(FD->getType()).isPODType(Context)) { Diag(FD->getLocation(), diag::err_flexible_array_has_nonpod_type) << FD->getDeclName() << FD->getType(); FD->setInvalidDecl(); @@ -8205,17 +8397,45 @@ void Sema::ActOnFields(Scope* S, FD->setInvalidDecl(); EnclosingDecl->setInvalidDecl(); continue; - } else if (getLangOptions().ObjC1 && + } + else if (!getLangOptions().CPlusPlus) { + if (getLangOptions().ObjCAutoRefCount && Record && !ARCErrReported) { + // It's an error in ARC if a field has lifetime. + // We don't want to report this in a system header, though, + // so we just make the field unavailable. + // FIXME: that's really not sufficient; we need to make the type + // itself invalid to, say, initialize or copy. + QualType T = FD->getType(); + Qualifiers::ObjCLifetime lifetime = T.getObjCLifetime(); + if (lifetime && lifetime != Qualifiers::OCL_ExplicitNone) { + SourceLocation loc = FD->getLocation(); + if (getSourceManager().isInSystemHeader(loc)) { + if (!FD->hasAttr<UnavailableAttr>()) { + FD->addAttr(new (Context) UnavailableAttr(loc, Context, + "this system field has retaining ownership")); + } + } else { + Diag(FD->getLocation(), diag::err_arc_objc_object_in_struct); + } + ARCErrReported = true; + } + } + else if (getLangOptions().ObjC1 && getLangOptions().getGCMode() != LangOptions::NonGC && - Record && - (FD->getType()->isObjCObjectPointerType() || - FD->getType().isObjCGCStrong())) - Record->setHasObjectMember(true); - else if (Context.getAsArrayType(FD->getType())) { - QualType BaseType = Context.getBaseElementType(FD->getType()); - if (Record && BaseType->isRecordType() && - BaseType->getAs<RecordType>()->getDecl()->hasObjectMember()) - Record->setHasObjectMember(true); + Record && !Record->hasObjectMember()) { + if (FD->getType()->isObjCObjectPointerType() || + FD->getType().isObjCGCStrong()) + Record->setHasObjectMember(true); + else if (Context.getAsArrayType(FD->getType())) { + QualType BaseType = Context.getBaseElementType(FD->getType()); + if (BaseType->isRecordType() && + BaseType->getAs<RecordType>()->getDecl()->hasObjectMember()) + Record->setHasObjectMember(true); + else if (BaseType->isObjCObjectPointerType() || + BaseType.isObjCGCStrong()) + Record->setHasObjectMember(true); + } + } } // Keep track of the number of named members. if (FD->getIdentifier()) @@ -8234,6 +8454,42 @@ void Sema::ActOnFields(Scope* S, Convs->setAccess(I, (*I)->getAccess()); if (!CXXRecord->isDependentType()) { + // Objective-C Automatic Reference Counting: + // If a class has a non-static data member of Objective-C pointer + // type (or array thereof), it is a non-POD type and its + // default constructor (if any), copy constructor, copy assignment + // operator, and destructor are non-trivial. + // + // This rule is also handled by CXXRecordDecl::completeDefinition(). + // However, here we check whether this particular class is only + // non-POD because of the presence of an Objective-C pointer member. + // If so, objects of this type cannot be shared between code compiled + // with instant objects and code compiled with manual retain/release. + if (getLangOptions().ObjCAutoRefCount && + CXXRecord->hasObjectMember() && + CXXRecord->getLinkage() == ExternalLinkage) { + if (CXXRecord->isPOD()) { + Diag(CXXRecord->getLocation(), + diag::warn_arc_non_pod_class_with_object_member) + << CXXRecord; + } else { + // FIXME: Fix-Its would be nice here, but finding a good location + // for them is going to be tricky. + if (CXXRecord->hasTrivialCopyConstructor()) + Diag(CXXRecord->getLocation(), + diag::warn_arc_trivial_member_function_with_object_member) + << CXXRecord << 0; + if (CXXRecord->hasTrivialCopyAssignment()) + Diag(CXXRecord->getLocation(), + diag::warn_arc_trivial_member_function_with_object_member) + << CXXRecord << 1; + if (CXXRecord->hasTrivialDestructor()) + Diag(CXXRecord->getLocation(), + diag::warn_arc_trivial_member_function_with_object_member) + << CXXRecord << 2; + } + } + // Adjust user-defined destructor exception spec. if (getLangOptions().CPlusPlus0x && CXXRecord->hasUserDeclaredDestructor()) @@ -8807,7 +9063,7 @@ void Sema::ActOnEnumBody(SourceLocation EnumLoc, SourceLocation LBraceLoc, // Adjust the Expr initializer and type. if (ECD->getInitExpr() && - !Context.hasSameType(NewTy, ECD->getInitExpr()->getType())) + !Context.hasSameType(NewTy, ECD->getInitExpr()->getType())) ECD->setInitExpr(ImplicitCastExpr::Create(Context, NewTy, CK_IntegralCast, ECD->getInitExpr(), diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp index 7f93ab72d6d1..61b7b3ee05e3 100644 --- a/lib/Sema/SemaDeclAttr.cpp +++ b/lib/Sema/SemaDeclAttr.cpp @@ -17,6 +17,7 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" +#include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/DelayedDiagnostic.h" @@ -48,14 +49,14 @@ enum { // Helper functions //===----------------------------------------------------------------------===// -static const FunctionType *getFunctionType(const Decl *d, +static const FunctionType *getFunctionType(const Decl *D, bool blocksToo = true) { QualType Ty; - if (const ValueDecl *decl = dyn_cast<ValueDecl>(d)) + if (const ValueDecl *decl = dyn_cast<ValueDecl>(D)) Ty = decl->getType(); - else if (const FieldDecl *decl = dyn_cast<FieldDecl>(d)) + else if (const FieldDecl *decl = dyn_cast<FieldDecl>(D)) Ty = decl->getType(); - else if (const TypedefNameDecl* decl = dyn_cast<TypedefNameDecl>(d)) + else if (const TypedefNameDecl* decl = dyn_cast<TypedefNameDecl>(D)) Ty = decl->getUnderlyingType(); else return 0; @@ -73,46 +74,47 @@ static const FunctionType *getFunctionType(const Decl *d, /// isFunction - Return true if the given decl has function /// type (function or function-typed variable). -static bool isFunction(const Decl *d) { - return getFunctionType(d, false) != NULL; +static bool isFunction(const Decl *D) { + return getFunctionType(D, false) != NULL; } /// isFunctionOrMethod - Return true if the given decl has function /// type (function or function-typed variable) or an Objective-C /// method. -static bool isFunctionOrMethod(const Decl *d) { - return isFunction(d)|| isa<ObjCMethodDecl>(d); +static bool isFunctionOrMethod(const Decl *D) { + return isFunction(D)|| isa<ObjCMethodDecl>(D); } /// isFunctionOrMethodOrBlock - Return true if the given decl has function /// type (function or function-typed variable) or an Objective-C /// method or a block. -static bool isFunctionOrMethodOrBlock(const Decl *d) { - if (isFunctionOrMethod(d)) +static bool isFunctionOrMethodOrBlock(const Decl *D) { + if (isFunctionOrMethod(D)) return true; // check for block is more involved. - if (const VarDecl *V = dyn_cast<VarDecl>(d)) { + if (const VarDecl *V = dyn_cast<VarDecl>(D)) { QualType Ty = V->getType(); return Ty->isBlockPointerType(); } - return isa<BlockDecl>(d); + return isa<BlockDecl>(D); } /// Return true if the given decl has a declarator that should have /// been processed by Sema::GetTypeForDeclarator. -static bool hasDeclarator(const Decl *d) { - // In some sense, TypedefNameDecl really *ought* to be a DeclaratorDecl. - return isa<DeclaratorDecl>(d) || isa<BlockDecl>(d) || isa<TypedefNameDecl>(d); +static bool hasDeclarator(const Decl *D) { + // In some sense, TypedefDecl really *ought* to be a DeclaratorDecl. + return isa<DeclaratorDecl>(D) || isa<BlockDecl>(D) || isa<TypedefNameDecl>(D) || + isa<ObjCPropertyDecl>(D); } /// hasFunctionProto - Return true if the given decl has a argument /// information. This decl should have already passed /// isFunctionOrMethod or isFunctionOrMethodOrBlock. -static bool hasFunctionProto(const Decl *d) { - if (const FunctionType *FnTy = getFunctionType(d)) +static bool hasFunctionProto(const Decl *D) { + if (const FunctionType *FnTy = getFunctionType(D)) return isa<FunctionProtoType>(FnTy); else { - assert(isa<ObjCMethodDecl>(d) || isa<BlockDecl>(d)); + assert(isa<ObjCMethodDecl>(D) || isa<BlockDecl>(D)); return true; } } @@ -120,42 +122,42 @@ static bool hasFunctionProto(const Decl *d) { /// getFunctionOrMethodNumArgs - Return number of function or method /// arguments. It is an error to call this on a K&R function (use /// hasFunctionProto first). -static unsigned getFunctionOrMethodNumArgs(const Decl *d) { - if (const FunctionType *FnTy = getFunctionType(d)) +static unsigned getFunctionOrMethodNumArgs(const Decl *D) { + if (const FunctionType *FnTy = getFunctionType(D)) return cast<FunctionProtoType>(FnTy)->getNumArgs(); - if (const BlockDecl *BD = dyn_cast<BlockDecl>(d)) + if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) return BD->getNumParams(); - return cast<ObjCMethodDecl>(d)->param_size(); + return cast<ObjCMethodDecl>(D)->param_size(); } -static QualType getFunctionOrMethodArgType(const Decl *d, unsigned Idx) { - if (const FunctionType *FnTy = getFunctionType(d)) +static QualType getFunctionOrMethodArgType(const Decl *D, unsigned Idx) { + if (const FunctionType *FnTy = getFunctionType(D)) return cast<FunctionProtoType>(FnTy)->getArgType(Idx); - if (const BlockDecl *BD = dyn_cast<BlockDecl>(d)) + if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) return BD->getParamDecl(Idx)->getType(); - return cast<ObjCMethodDecl>(d)->param_begin()[Idx]->getType(); + return cast<ObjCMethodDecl>(D)->param_begin()[Idx]->getType(); } -static QualType getFunctionOrMethodResultType(const Decl *d) { - if (const FunctionType *FnTy = getFunctionType(d)) +static QualType getFunctionOrMethodResultType(const Decl *D) { + if (const FunctionType *FnTy = getFunctionType(D)) return cast<FunctionProtoType>(FnTy)->getResultType(); - return cast<ObjCMethodDecl>(d)->getResultType(); + return cast<ObjCMethodDecl>(D)->getResultType(); } -static bool isFunctionOrMethodVariadic(const Decl *d) { - if (const FunctionType *FnTy = getFunctionType(d)) { +static bool isFunctionOrMethodVariadic(const Decl *D) { + if (const FunctionType *FnTy = getFunctionType(D)) { const FunctionProtoType *proto = cast<FunctionProtoType>(FnTy); return proto->isVariadic(); - } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(d)) + } else if (const BlockDecl *BD = dyn_cast<BlockDecl>(D)) return BD->isVariadic(); else { - return cast<ObjCMethodDecl>(d)->isVariadic(); + return cast<ObjCMethodDecl>(D)->isVariadic(); } } -static bool isInstanceMethod(const Decl *d) { - if (const CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(d)) +static bool isInstanceMethod(const Decl *D) { + if (const CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(D)) return MethodDecl->isInstance(); return false; } @@ -192,6 +194,16 @@ static inline bool isCFStringType(QualType T, ASTContext &Ctx) { return RD->getIdentifier() == &Ctx.Idents.get("__CFString"); } +static bool checkAttributeNumArgs(Sema &S, const AttributeList &Attr, + unsigned int Num) { + if (Attr.getNumArgs() != Num) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << Num; + return false; + } + + return true; +} + //===----------------------------------------------------------------------===// // Attribute Implementations //===----------------------------------------------------------------------===// @@ -200,9 +212,9 @@ static inline bool isCFStringType(QualType T, ASTContext &Ctx) { // least add some helper functions to check most argument patterns (# // and types of args). -static void HandleExtVectorTypeAttr(Scope *scope, Decl *d, - const AttributeList &Attr, Sema &S) { - TypedefNameDecl *tDecl = dyn_cast<TypedefNameDecl>(d); +static void handleExtVectorTypeAttr(Sema &S, Scope *scope, Decl *D, + const AttributeList &Attr) { + TypedefNameDecl *tDecl = dyn_cast<TypedefNameDecl>(D); if (tDecl == 0) { S.Diag(Attr.getLoc(), diag::err_typecheck_ext_vector_not_typedef); return; @@ -217,13 +229,17 @@ static void HandleExtVectorTypeAttr(Scope *scope, Decl *d, CXXScopeSpec SS; UnqualifiedId id; id.setIdentifier(Attr.getParameterName(), Attr.getLoc()); - sizeExpr = S.ActOnIdExpression(scope, SS, id, false, false).takeAs<Expr>(); + + ExprResult Size = S.ActOnIdExpression(scope, SS, id, false, false); + if (Size.isInvalid()) + return; + + sizeExpr = Size.get(); } else { // check the attribute arguments. - if (Attr.getNumArgs() != 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + if (!checkAttributeNumArgs(S, Attr, 1)) return; - } + sizeExpr = Attr.getArg(0); } @@ -239,16 +255,14 @@ static void HandleExtVectorTypeAttr(Scope *scope, Decl *d, } } -static void HandlePackedAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handlePackedAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() > 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (TagDecl *TD = dyn_cast<TagDecl>(d)) + if (TagDecl *TD = dyn_cast<TagDecl>(D)) TD->addAttr(::new (S.Context) PackedAttr(Attr.getLoc(), S.Context)); - else if (FieldDecl *FD = dyn_cast<FieldDecl>(d)) { + else if (FieldDecl *FD = dyn_cast<FieldDecl>(D)) { // If the alignment is less than or equal to 8 bits, the packed attribute // has no effect. if (!FD->getType()->isIncompleteType() && @@ -261,49 +275,45 @@ static void HandlePackedAttr(Decl *d, const AttributeList &Attr, Sema &S) { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); } -static void HandleMsStructAttr(Decl *d, const AttributeList &Attr, Sema &S) { - if (TagDecl *TD = dyn_cast<TagDecl>(d)) +static void handleMsStructAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (TagDecl *TD = dyn_cast<TagDecl>(D)) TD->addAttr(::new (S.Context) MsStructAttr(Attr.getLoc(), S.Context)); else S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); } -static void HandleIBAction(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleIBAction(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() > 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } // The IBAction attributes only apply to instance methods. - if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(d)) + if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) if (MD->isInstanceMethod()) { - d->addAttr(::new (S.Context) IBActionAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) IBActionAttr(Attr.getLoc(), S.Context)); return; } S.Diag(Attr.getLoc(), diag::warn_attribute_ibaction) << Attr.getName(); } -static void HandleIBOutlet(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleIBOutlet(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() > 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } // The IBOutlet attributes only apply to instance variables of // Objective-C classes. - if (isa<ObjCIvarDecl>(d) || isa<ObjCPropertyDecl>(d)) { - d->addAttr(::new (S.Context) IBOutletAttr(Attr.getLoc(), S.Context)); + if (isa<ObjCIvarDecl>(D) || isa<ObjCPropertyDecl>(D)) { + D->addAttr(::new (S.Context) IBOutletAttr(Attr.getLoc(), S.Context)); return; } S.Diag(Attr.getLoc(), diag::warn_attribute_iboutlet) << Attr.getName(); } -static void HandleIBOutletCollection(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleIBOutletCollection(Sema &S, Decl *D, + const AttributeList &Attr) { // The iboutletcollection attribute can have zero or one arguments. if (Attr.getParameterName() && Attr.getNumArgs() > 0) { @@ -313,17 +323,17 @@ static void HandleIBOutletCollection(Decl *d, const AttributeList &Attr, // The IBOutletCollection attributes only apply to instance variables of // Objective-C classes. - if (!(isa<ObjCIvarDecl>(d) || isa<ObjCPropertyDecl>(d))) { + if (!(isa<ObjCIvarDecl>(D) || isa<ObjCPropertyDecl>(D))) { S.Diag(Attr.getLoc(), diag::warn_attribute_iboutlet) << Attr.getName(); return; } - if (const ValueDecl *VD = dyn_cast<ValueDecl>(d)) + if (const ValueDecl *VD = dyn_cast<ValueDecl>(D)) if (!VD->getType()->getAs<ObjCObjectPointerType>()) { S.Diag(Attr.getLoc(), diag::err_iboutletcollection_object_type) << VD->getType() << 0; return; } - if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(d)) + if (const ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) if (!PD->getType()->getAs<ObjCObjectPointerType>()) { S.Diag(Attr.getLoc(), diag::err_iboutletcollection_object_type) << PD->getType() << 1; @@ -335,7 +345,7 @@ static void HandleIBOutletCollection(Decl *d, const AttributeList &Attr, II = &S.Context.Idents.get("id"); ParsedType TypeRep = S.getTypeName(*II, Attr.getLoc(), - S.getScopeForContext(d->getDeclContext()->getParent())); + S.getScopeForContext(D->getDeclContext()->getParent())); if (!TypeRep) { S.Diag(Attr.getLoc(), diag::err_iboutletcollection_type) << II; return; @@ -350,14 +360,29 @@ static void HandleIBOutletCollection(Decl *d, const AttributeList &Attr, S.Diag(Attr.getLoc(), diag::err_iboutletcollection_type) << II; return; } - d->addAttr(::new (S.Context) IBOutletCollectionAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) IBOutletCollectionAttr(Attr.getLoc(), S.Context, QT)); } -static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void possibleTransparentUnionPointerType(QualType &T) { + if (const RecordType *UT = T->getAsUnionType()) + if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) { + RecordDecl *UD = UT->getDecl(); + for (RecordDecl::field_iterator it = UD->field_begin(), + itend = UD->field_end(); it != itend; ++it) { + QualType QT = it->getType(); + if (QT->isAnyPointerType() || QT->isBlockPointerType()) { + T = QT; + return; + } + } + } +} + +static void handleNonNullAttr(Sema &S, Decl *D, const AttributeList &Attr) { // GCC ignores the nonnull attribute on K&R style function prototypes, so we // ignore it as well - if (!isFunctionOrMethod(d) || !hasFunctionProto(d)) { + if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; @@ -365,8 +390,8 @@ static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) { // In C++ the implicit 'this' function parameter also counts, and they are // counted from one. - bool HasImplicitThisParam = isInstanceMethod(d); - unsigned NumArgs = getFunctionOrMethodNumArgs(d) + HasImplicitThisParam; + bool HasImplicitThisParam = isInstanceMethod(D); + unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam; // The nonnull attribute only applies to pointers. llvm::SmallVector<unsigned, 10> NonNullArgs; @@ -405,7 +430,9 @@ static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // Is the function argument a pointer type? - QualType T = getFunctionOrMethodArgType(d, x).getNonReferenceType(); + QualType T = getFunctionOrMethodArgType(D, x).getNonReferenceType(); + possibleTransparentUnionPointerType(T); + if (!T->isAnyPointerType() && !T->isBlockPointerType()) { // FIXME: Should also highlight argument in decl. S.Diag(Attr.getLoc(), diag::warn_nonnull_pointers_only) @@ -419,23 +446,11 @@ static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) { // If no arguments were specified to __attribute__((nonnull)) then all pointer // arguments have a nonnull attribute. if (NonNullArgs.empty()) { - for (unsigned I = 0, E = getFunctionOrMethodNumArgs(d); I != E; ++I) { - QualType T = getFunctionOrMethodArgType(d, I).getNonReferenceType(); + for (unsigned I = 0, E = getFunctionOrMethodNumArgs(D); I != E; ++I) { + QualType T = getFunctionOrMethodArgType(D, I).getNonReferenceType(); + possibleTransparentUnionPointerType(T); if (T->isAnyPointerType() || T->isBlockPointerType()) NonNullArgs.push_back(I); - else if (const RecordType *UT = T->getAsUnionType()) { - if (UT && UT->getDecl()->hasAttr<TransparentUnionAttr>()) { - RecordDecl *UD = UT->getDecl(); - for (RecordDecl::field_iterator it = UD->field_begin(), - itend = UD->field_end(); it != itend; ++it) { - T = it->getType(); - if (T->isAnyPointerType() || T->isBlockPointerType()) { - NonNullArgs.push_back(I); - break; - } - } - } - } } // No pointer arguments? @@ -451,11 +466,11 @@ static void HandleNonNullAttr(Decl *d, const AttributeList &Attr, Sema &S) { unsigned* start = &NonNullArgs[0]; unsigned size = NonNullArgs.size(); llvm::array_pod_sort(start, start + size); - d->addAttr(::new (S.Context) NonNullAttr(Attr.getLoc(), S.Context, start, + D->addAttr(::new (S.Context) NonNullAttr(Attr.getLoc(), S.Context, start, size)); } -static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) { +static void handleOwnershipAttr(Sema &S, Decl *D, const AttributeList &AL) { // This attribute must be applied to a function declaration. // The first argument to the attribute must be a string, // the name of the resource, for example "malloc". @@ -500,7 +515,7 @@ static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) { llvm_unreachable("Unknown ownership attribute"); } - if (!isFunction(d) || !hasFunctionProto(d)) { + if (!isFunction(D) || !hasFunctionProto(D)) { S.Diag(AL.getLoc(), diag::warn_attribute_wrong_decl_type) << AL.getName() << ExpectedFunction; return; @@ -508,8 +523,8 @@ static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) { // In C++ the implicit 'this' function parameter also counts, and they are // counted from one. - bool HasImplicitThisParam = isInstanceMethod(d); - unsigned NumArgs = getFunctionOrMethodNumArgs(d) + HasImplicitThisParam; + bool HasImplicitThisParam = isInstanceMethod(D); + unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam; llvm::StringRef Module = AL.getParameterName()->getName(); @@ -552,7 +567,7 @@ static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) { case OwnershipAttr::Takes: case OwnershipAttr::Holds: { // Is the function argument a pointer type? - QualType T = getFunctionOrMethodArgType(d, x); + QualType T = getFunctionOrMethodArgType(D, x); if (!T->isAnyPointerType() && !T->isBlockPointerType()) { // FIXME: Should also highlight argument in decl. S.Diag(AL.getLoc(), diag::err_ownership_type) @@ -584,8 +599,8 @@ static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) { // Check we don't have a conflict with another ownership attribute. for (specific_attr_iterator<OwnershipAttr> - i = d->specific_attr_begin<OwnershipAttr>(), - e = d->specific_attr_end<OwnershipAttr>(); + i = D->specific_attr_begin<OwnershipAttr>(), + e = D->specific_attr_end<OwnershipAttr>(); i != e; ++i) { if ((*i)->getOwnKind() != K) { for (const unsigned *I = (*i)->args_begin(), *E = (*i)->args_end(); @@ -609,7 +624,7 @@ static void HandleOwnershipAttr(Decl *d, const AttributeList &AL, Sema &S) { return; } - d->addAttr(::new (S.Context) OwnershipAttr(AL.getLoc(), S.Context, K, Module, + D->addAttr(::new (S.Context) OwnershipAttr(AL.getLoc(), S.Context, K, Module, start, size)); } @@ -633,20 +648,20 @@ static bool hasEffectivelyInternalLinkage(NamedDecl *D) { return false; } -static void HandleWeakRefAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleWeakRefAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. if (Attr.getNumArgs() > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; return; } - if (!isa<VarDecl>(d) && !isa<FunctionDecl>(d)) { + if (!isa<VarDecl>(D) && !isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariableOrFunction; return; } - NamedDecl *nd = cast<NamedDecl>(d); + NamedDecl *nd = cast<NamedDecl>(D); // gcc rejects // class c { @@ -658,7 +673,7 @@ static void HandleWeakRefAttr(Decl *d, const AttributeList &Attr, Sema &S) { // static int a __attribute__((weakref ("v2"))); // } // we reject them - const DeclContext *Ctx = d->getDeclContext()->getRedeclContext(); + const DeclContext *Ctx = D->getDeclContext()->getRedeclContext(); if (!Ctx->isFileContext()) { S.Diag(Attr.getLoc(), diag::err_attribute_weakref_not_global_context) << nd->getNameAsString(); @@ -704,14 +719,14 @@ static void HandleWeakRefAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // GCC will accept anything as the argument of weakref. Should we // check for an existing decl? - d->addAttr(::new (S.Context) AliasAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) AliasAttr(Attr.getLoc(), S.Context, Str->getString())); } - d->addAttr(::new (S.Context) WeakRefAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) WeakRefAttr(Attr.getLoc(), S.Context)); } -static void HandleAliasAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.getNumArgs() != 1) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; @@ -735,55 +750,52 @@ static void HandleAliasAttr(Decl *d, const AttributeList &Attr, Sema &S) { // FIXME: check if target symbol exists in current file - d->addAttr(::new (S.Context) AliasAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) AliasAttr(Attr.getLoc(), S.Context, Str->getString())); } -static void HandleNakedAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleNakedAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isa<FunctionDecl>(d)) { + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) NakedAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) NakedAttr(Attr.getLoc(), S.Context)); } -static void HandleAlwaysInlineAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleAlwaysInlineAttr(Sema &S, Decl *D, + const AttributeList &Attr) { // Check the attribute arguments. if (Attr.hasParameterOrArguments()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - if (!isa<FunctionDecl>(d)) { + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) AlwaysInlineAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) AlwaysInlineAttr(Attr.getLoc(), S.Context)); } -static void HandleMallocAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleMallocAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Check the attribute arguments. if (Attr.hasParameterOrArguments()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(d)) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { QualType RetTy = FD->getResultType(); if (RetTy->isAnyPointerType() || RetTy->isBlockPointerType()) { - d->addAttr(::new (S.Context) MallocAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) MallocAttr(Attr.getLoc(), S.Context)); return; } } @@ -791,46 +803,44 @@ static void HandleMallocAttr(Decl *d, const AttributeList &Attr, Sema &S) { S.Diag(Attr.getLoc(), diag::warn_attribute_malloc_pointer_only); } -static void HandleMayAliasAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleMayAliasAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - d->addAttr(::new (S.Context) MayAliasAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) MayAliasAttr(Attr.getLoc(), S.Context)); } -static void HandleNoCommonAttr(Decl *d, const AttributeList &Attr, Sema &S) { - assert(Attr.isInvalid() == false); - if (isa<VarDecl>(d)) - d->addAttr(::new (S.Context) NoCommonAttr(Attr.getLoc(), S.Context)); +static void handleNoCommonAttr(Sema &S, Decl *D, const AttributeList &Attr) { + assert(!Attr.isInvalid()); + if (isa<VarDecl>(D)) + D->addAttr(::new (S.Context) NoCommonAttr(Attr.getLoc(), S.Context)); else S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariable; } -static void HandleCommonAttr(Decl *d, const AttributeList &Attr, Sema &S) { - assert(Attr.isInvalid() == false); - if (isa<VarDecl>(d)) - d->addAttr(::new (S.Context) CommonAttr(Attr.getLoc(), S.Context)); +static void handleCommonAttr(Sema &S, Decl *D, const AttributeList &Attr) { + assert(!Attr.isInvalid()); + if (isa<VarDecl>(D)) + D->addAttr(::new (S.Context) CommonAttr(Attr.getLoc(), S.Context)); else S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariable; } -static void HandleNoReturnAttr(Decl *d, const AttributeList &attr, Sema &S) { - if (hasDeclarator(d)) return; +static void handleNoReturnAttr(Sema &S, Decl *D, const AttributeList &attr) { + if (hasDeclarator(D)) return; if (S.CheckNoReturnAttr(attr)) return; - if (!isa<ObjCMethodDecl>(d)) { + if (!isa<ObjCMethodDecl>(D)) { S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) << attr.getName() << ExpectedFunctionOrMethod; return; } - d->addAttr(::new (S.Context) NoReturnAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) NoReturnAttr(attr.getLoc(), S.Context)); } bool Sema::CheckNoReturnAttr(const AttributeList &attr) { @@ -843,19 +853,17 @@ bool Sema::CheckNoReturnAttr(const AttributeList &attr) { return false; } -static void HandleAnalyzerNoReturnAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleAnalyzerNoReturnAttr(Sema &S, Decl *D, + const AttributeList &Attr) { // The checking path for 'noreturn' and 'analyzer_noreturn' are different // because 'analyzer_noreturn' does not impact the type. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; - return; - } + if(!checkAttributeNumArgs(S, Attr, 0)) + return; - if (!isFunctionOrMethod(d) && !isa<BlockDecl>(d)) { - ValueDecl *VD = dyn_cast<ValueDecl>(d); + if (!isFunctionOrMethod(D) && !isa<BlockDecl>(D)) { + ValueDecl *VD = dyn_cast<ValueDecl>(D); if (VD == 0 || (!VD->getType()->isBlockPointerType() && !VD->getType()->isFunctionPointerType())) { S.Diag(Attr.getLoc(), @@ -866,12 +874,11 @@ static void HandleAnalyzerNoReturnAttr(Decl *d, const AttributeList &Attr, } } - d->addAttr(::new (S.Context) AnalyzerNoReturnAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) AnalyzerNoReturnAttr(Attr.getLoc(), S.Context)); } // PS3 PPU-specific. -static void HandleVecReturnAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleVecReturnAttr(Sema &S, Decl *D, const AttributeList &Attr) { /* Returning a Vector Class in Registers @@ -895,18 +902,18 @@ static void HandleVecReturnAttr(Decl *d, const AttributeList &Attr, return result; // This will be returned in a register } */ - if (!isa<RecordDecl>(d)) { + if (!isa<RecordDecl>(D)) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << Attr.getName() << ExpectedClass; return; } - if (d->getAttr<VecReturnAttr>()) { + if (D->getAttr<VecReturnAttr>()) { S.Diag(Attr.getLoc(), diag::err_repeat_attribute) << "vecreturn"; return; } - RecordDecl *record = cast<RecordDecl>(d); + RecordDecl *record = cast<RecordDecl>(D); int count = 0; if (!isa<CXXRecordDecl>(record)) { @@ -928,11 +935,11 @@ static void HandleVecReturnAttr(Decl *d, const AttributeList &Attr, count++; } - d->addAttr(::new (S.Context) VecReturnAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) VecReturnAttr(Attr.getLoc(), S.Context)); } -static void HandleDependencyAttr(Decl *d, const AttributeList &Attr, Sema &S) { - if (!isFunctionOrMethod(d) && !isa<ParmVarDecl>(d)) { +static void handleDependencyAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!isFunctionOrMethod(D) && !isa<ParmVarDecl>(D)) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionMethodOrParameter; return; @@ -940,45 +947,45 @@ static void HandleDependencyAttr(Decl *d, const AttributeList &Attr, Sema &S) { // FIXME: Actually store the attribute on the declaration } -static void HandleUnusedAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleUnusedAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.hasParameterOrArguments()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - if (!isa<VarDecl>(d) && !isa<ObjCIvarDecl>(d) && !isFunctionOrMethod(d) && - !isa<TypeDecl>(d) && !isa<LabelDecl>(d)) { + if (!isa<VarDecl>(D) && !isa<ObjCIvarDecl>(D) && !isFunctionOrMethod(D) && + !isa<TypeDecl>(D) && !isa<LabelDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariableFunctionOrLabel; return; } - d->addAttr(::new (S.Context) UnusedAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) UnusedAttr(Attr.getLoc(), S.Context)); } -static void HandleUsedAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleUsedAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.hasParameterOrArguments()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - if (const VarDecl *VD = dyn_cast<VarDecl>(d)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(D)) { if (VD->hasLocalStorage() || VD->hasExternalStorage()) { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "used"; return; } - } else if (!isFunctionOrMethod(d)) { + } else if (!isFunctionOrMethod(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariableOrFunction; return; } - d->addAttr(::new (S.Context) UsedAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) UsedAttr(Attr.getLoc(), S.Context)); } -static void HandleConstructorAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleConstructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.getNumArgs() > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1; @@ -998,17 +1005,17 @@ static void HandleConstructorAttr(Decl *d, const AttributeList &Attr, Sema &S) { priority = Idx.getZExtValue(); } - if (!isa<FunctionDecl>(d)) { + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) ConstructorAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) ConstructorAttr(Attr.getLoc(), S.Context, priority)); } -static void HandleDestructorAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleDestructorAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.getNumArgs() > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1; @@ -1028,17 +1035,17 @@ static void HandleDestructorAttr(Decl *d, const AttributeList &Attr, Sema &S) { priority = Idx.getZExtValue(); } - if (!isa<FunctionDecl>(d)) { + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) DestructorAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) DestructorAttr(Attr.getLoc(), S.Context, priority)); } -static void HandleDeprecatedAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleDeprecatedAttr(Sema &S, Decl *D, const AttributeList &Attr) { unsigned NumArgs = Attr.getNumArgs(); if (NumArgs > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1; @@ -1057,10 +1064,10 @@ static void HandleDeprecatedAttr(Decl *d, const AttributeList &Attr, Sema &S) { Str = SE->getString(); } - d->addAttr(::new (S.Context) DeprecatedAttr(Attr.getLoc(), S.Context, Str)); + D->addAttr(::new (S.Context) DeprecatedAttr(Attr.getLoc(), S.Context, Str)); } -static void HandleUnavailableAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleUnavailableAttr(Sema &S, Decl *D, const AttributeList &Attr) { unsigned NumArgs = Attr.getNumArgs(); if (NumArgs > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 1; @@ -1078,11 +1085,23 @@ static void HandleUnavailableAttr(Decl *d, const AttributeList &Attr, Sema &S) { } Str = SE->getString(); } - d->addAttr(::new (S.Context) UnavailableAttr(Attr.getLoc(), S.Context, Str)); + D->addAttr(::new (S.Context) UnavailableAttr(Attr.getLoc(), S.Context, Str)); +} + +static void handleArcWeakrefUnavailableAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + unsigned NumArgs = Attr.getNumArgs(); + if (NumArgs > 0) { + S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 0; + return; + } + + D->addAttr(::new (S.Context) ArcWeakrefUnavailableAttr( + Attr.getLoc(), S.Context)); } -static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleAvailabilityAttr(Sema &S, Decl *D, + const AttributeList &Attr) { IdentifierInfo *Platform = Attr.getParameterName(); SourceLocation PlatformLoc = Attr.getParameterLoc(); @@ -1126,7 +1145,7 @@ static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, return; } - d->addAttr(::new (S.Context) AvailabilityAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) AvailabilityAttr(Attr.getLoc(), S.Context, Platform, Introduced.Version, Deprecated.Version, @@ -1134,12 +1153,10 @@ static void HandleAvailabilityAttr(Decl *d, const AttributeList &Attr, IsUnavailable)); } -static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleVisibilityAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + if(!checkAttributeNumArgs(S, Attr, 1)) return; - } Expr *Arg = Attr.getArg(0); Arg = Arg->IgnoreParenCasts(); @@ -1167,30 +1184,30 @@ static void HandleVisibilityAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) VisibilityAttr(Attr.getLoc(), S.Context, type)); + D->addAttr(::new (S.Context) VisibilityAttr(Attr.getLoc(), S.Context, type)); } -static void HandleObjCMethodFamilyAttr(Decl *decl, const AttributeList &attr, - Sema &S) { +static void handleObjCMethodFamilyAttr(Sema &S, Decl *decl, + const AttributeList &Attr) { ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(decl); if (!method) { - S.Diag(attr.getLoc(), diag::err_attribute_wrong_decl_type) + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_decl_type) << ExpectedMethod; return; } - if (attr.getNumArgs() != 0 || !attr.getParameterName()) { - if (!attr.getParameterName() && attr.getNumArgs() == 1) { - S.Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string) + if (Attr.getNumArgs() != 0 || !Attr.getParameterName()) { + if (!Attr.getParameterName() && Attr.getNumArgs() == 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_string) << "objc_method_family" << 1; } else { - S.Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; } - attr.setInvalid(); + Attr.setInvalid(); return; } - llvm::StringRef param = attr.getParameterName()->getName(); + llvm::StringRef param = Attr.getParameterName()->getName(); ObjCMethodFamilyAttr::FamilyKind family; if (param == "none") family = ObjCMethodFamilyAttr::OMF_None; @@ -1207,20 +1224,26 @@ static void HandleObjCMethodFamilyAttr(Decl *decl, const AttributeList &attr, else { // Just warn and ignore it. This is future-proof against new // families being used in system headers. - S.Diag(attr.getParameterLoc(), diag::warn_unknown_method_family); + S.Diag(Attr.getParameterLoc(), diag::warn_unknown_method_family); return; } - decl->addAttr(new (S.Context) ObjCMethodFamilyAttr(attr.getLoc(), - S.Context, family)); + if (family == ObjCMethodFamilyAttr::OMF_init && + !method->getResultType()->isObjCObjectPointerType()) { + S.Diag(method->getLocation(), diag::err_init_method_bad_return_type) + << method->getResultType(); + // Ignore the attribute. + return; + } + + method->addAttr(new (S.Context) ObjCMethodFamilyAttr(Attr.getLoc(), + S.Context, family)); } -static void HandleObjCExceptionAttr(Decl *D, const AttributeList &Attr, - Sema &S) { - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; +static void handleObjCExceptionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } ObjCInterfaceDecl *OCI = dyn_cast<ObjCInterfaceDecl>(D); if (OCI == 0) { @@ -1231,7 +1254,7 @@ static void HandleObjCExceptionAttr(Decl *D, const AttributeList &Attr, D->addAttr(::new (S.Context) ObjCExceptionAttr(Attr.getLoc(), S.Context)); } -static void HandleObjCNSObject(Decl *D, const AttributeList &Attr, Sema &S) { +static void handleObjCNSObject(Sema &S, Decl *D, const AttributeList &Attr) { if (Attr.getNumArgs() != 0) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; return; @@ -1248,7 +1271,7 @@ static void HandleObjCNSObject(Decl *D, const AttributeList &Attr, Sema &S) { } static void -HandleOverloadableAttr(Decl *D, const AttributeList &Attr, Sema &S) { +handleOverloadableAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (Attr.getNumArgs() != 0) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; return; @@ -1262,7 +1285,7 @@ HandleOverloadableAttr(Decl *D, const AttributeList &Attr, Sema &S) { D->addAttr(::new (S.Context) OverloadableAttr(Attr.getLoc(), S.Context)); } -static void HandleBlocksAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleBlocksAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (!Attr.getParameterName()) { S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_string) << "blocks" << 1; @@ -1283,10 +1306,10 @@ static void HandleBlocksAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) BlocksAttr(Attr.getLoc(), S.Context, type)); + D->addAttr(::new (S.Context) BlocksAttr(Attr.getLoc(), S.Context, type)); } -static void HandleSentinelAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleSentinelAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.getNumArgs() > 2) { S.Diag(Attr.getLoc(), diag::err_attribute_too_many_arguments) << 2; @@ -1333,7 +1356,7 @@ static void HandleSentinelAttr(Decl *d, const AttributeList &Attr, Sema &S) { } } - if (FunctionDecl *FD = dyn_cast<FunctionDecl>(d)) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { const FunctionType *FT = FD->getType()->getAs<FunctionType>(); assert(FT && "FunctionDecl has non-function type?"); @@ -1346,19 +1369,19 @@ static void HandleSentinelAttr(Decl *d, const AttributeList &Attr, Sema &S) { S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_not_variadic) << 0; return; } - } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(d)) { + } else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { if (!MD->isVariadic()) { S.Diag(Attr.getLoc(), diag::warn_attribute_sentinel_not_variadic) << 0; return; } - } else if (isa<BlockDecl>(d)) { + } else if (isa<BlockDecl>(D)) { // Note! BlockDecl is typeless. Variadic diagnostics will be issued by the // caller. ; - } else if (const VarDecl *V = dyn_cast<VarDecl>(d)) { + } else if (const VarDecl *V = dyn_cast<VarDecl>(D)) { QualType Ty = V->getType(); if (Ty->isBlockPointerType() || Ty->isFunctionPointerType()) { - const FunctionType *FT = Ty->isFunctionPointerType() ? getFunctionType(d) + const FunctionType *FT = Ty->isFunctionPointerType() ? getFunctionType(D) : Ty->getAs<BlockPointerType>()->getPointeeType()->getAs<FunctionType>(); if (!cast<FunctionProtoType>(FT)->isVariadic()) { int m = Ty->isFunctionPointerType() ? 0 : 1; @@ -1375,16 +1398,14 @@ static void HandleSentinelAttr(Decl *d, const AttributeList &Attr, Sema &S) { << Attr.getName() << ExpectedFunctionMethodOrBlock; return; } - d->addAttr(::new (S.Context) SentinelAttr(Attr.getLoc(), S.Context, sentinel, + D->addAttr(::new (S.Context) SentinelAttr(Attr.getLoc(), S.Context, sentinel, nullPos)); } -static void HandleWarnUnusedResult(Decl *D, const AttributeList &Attr, Sema &S) { +static void handleWarnUnusedResult(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } if (!isFunction(D) && !isa<ObjCMethodDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) @@ -1407,36 +1428,35 @@ static void HandleWarnUnusedResult(Decl *D, const AttributeList &Attr, Sema &S) D->addAttr(::new (S.Context) WarnUnusedResultAttr(Attr.getLoc(), S.Context)); } -static void HandleWeakAttr(Decl *d, const AttributeList &attr, Sema &S) { +static void handleWeakAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (attr.hasParameterOrArguments()) { - S.Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (Attr.hasParameterOrArguments()) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - if (!isa<VarDecl>(d) && !isa<FunctionDecl>(d)) { - S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << attr.getName() << ExpectedVariableOrFunction; + if (!isa<VarDecl>(D) && !isa<FunctionDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedVariableOrFunction; return; } - NamedDecl *nd = cast<NamedDecl>(d); + NamedDecl *nd = cast<NamedDecl>(D); // 'weak' only applies to declarations with external linkage. if (hasEffectivelyInternalLinkage(nd)) { - S.Diag(attr.getLoc(), diag::err_attribute_weak_static); + S.Diag(Attr.getLoc(), diag::err_attribute_weak_static); return; } - nd->addAttr(::new (S.Context) WeakAttr(attr.getLoc(), S.Context)); + nd->addAttr(::new (S.Context) WeakAttr(Attr.getLoc(), S.Context)); } -static void HandleWeakImportAttr(Decl *D, const AttributeList &Attr, Sema &S) { +static void handleWeakImportAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } + // weak_import only applies to variable & function declarations. bool isDef = false; @@ -1459,13 +1479,11 @@ static void HandleWeakImportAttr(Decl *D, const AttributeList &Attr, Sema &S) { D->addAttr(::new (S.Context) WeakImportAttr(Attr.getLoc(), S.Context)); } -static void HandleReqdWorkGroupSize(Decl *D, const AttributeList &Attr, - Sema &S) { +static void handleReqdWorkGroupSize(Sema &S, Decl *D, + const AttributeList &Attr) { // Attribute has 3 arguments. - if (Attr.getNumArgs() != 3) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + if (!checkAttributeNumArgs(S, Attr, 3)) return; - } unsigned WGSize[3]; for (unsigned i = 0; i < 3; ++i) { @@ -1484,12 +1502,10 @@ static void HandleReqdWorkGroupSize(Decl *D, const AttributeList &Attr, WGSize[2])); } -static void HandleSectionAttr(Decl *D, const AttributeList &Attr, Sema &S) { +static void handleSectionAttr(Sema &S, Decl *D, const AttributeList &Attr) { // Attribute has no arguments. - if (Attr.getNumArgs() != 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + if (!checkAttributeNumArgs(S, Attr, 1)) return; - } // Make sure that there is a string literal as the sections's single // argument. @@ -1519,37 +1535,45 @@ static void HandleSectionAttr(Decl *D, const AttributeList &Attr, Sema &S) { } -static void HandleNothrowAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleNothrowAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.hasParameterOrArguments()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - - d->addAttr(::new (S.Context) NoThrowAttr(Attr.getLoc(), S.Context)); + + if (NoThrowAttr *Existing = D->getAttr<NoThrowAttr>()) { + if (Existing->getLocation().isInvalid()) + Existing->setLocation(Attr.getLoc()); + } else { + D->addAttr(::new (S.Context) NoThrowAttr(Attr.getLoc(), S.Context)); + } } -static void HandleConstAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleConstAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.hasParameterOrArguments()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; return; } - d->addAttr(::new (S.Context) ConstAttr(Attr.getLoc(), S.Context)); + if (ConstAttr *Existing = D->getAttr<ConstAttr>()) { + if (Existing->getLocation().isInvalid()) + Existing->setLocation(Attr.getLoc()); + } else { + D->addAttr(::new (S.Context) ConstAttr(Attr.getLoc(), S.Context)); + } } -static void HandlePureAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handlePureAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - d->addAttr(::new (S.Context) PureAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) PureAttr(Attr.getLoc(), S.Context)); } -static void HandleCleanupAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleCleanupAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (!Attr.getParameterName()) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; return; @@ -1560,7 +1584,7 @@ static void HandleCleanupAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - VarDecl *VD = dyn_cast<VarDecl>(d); + VarDecl *VD = dyn_cast<VarDecl>(D); if (!VD || !VD->hasLocalStorage()) { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "cleanup"; @@ -1605,18 +1629,17 @@ static void HandleCleanupAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) CleanupAttr(Attr.getLoc(), S.Context, FD)); + D->addAttr(::new (S.Context) CleanupAttr(Attr.getLoc(), S.Context, FD)); S.MarkDeclarationReferenced(Attr.getParameterLoc(), FD); } /// Handle __attribute__((format_arg((idx)))) attribute based on /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html -static void HandleFormatArgAttr(Decl *d, const AttributeList &Attr, Sema &S) { - if (Attr.getNumArgs() != 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; +static void handleFormatArgAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (!checkAttributeNumArgs(S, Attr, 1)) return; - } - if (!isFunctionOrMethod(d) || !hasFunctionProto(d)) { + + if (!isFunctionOrMethod(D) || !hasFunctionProto(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; @@ -1624,8 +1647,8 @@ static void HandleFormatArgAttr(Decl *d, const AttributeList &Attr, Sema &S) { // In C++ the implicit 'this' function parameter also counts, and they are // counted from one. - bool HasImplicitThisParam = isInstanceMethod(d); - unsigned NumArgs = getFunctionOrMethodNumArgs(d) + HasImplicitThisParam; + bool HasImplicitThisParam = isInstanceMethod(D); + unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam; unsigned FirstIdx = 1; // checks for the 2nd argument @@ -1656,7 +1679,7 @@ static void HandleFormatArgAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // make sure the format string is really a string - QualType Ty = getFunctionOrMethodArgType(d, ArgIdx); + QualType Ty = getFunctionOrMethodArgType(D, ArgIdx); bool not_nsstring_type = !isNSStringType(Ty, S.Context); if (not_nsstring_type && @@ -1669,7 +1692,7 @@ static void HandleFormatArgAttr(Decl *d, const AttributeList &Attr, Sema &S) { << IdxExpr->getSourceRange(); return; } - Ty = getFunctionOrMethodResultType(d); + Ty = getFunctionOrMethodResultType(D); if (!isNSStringType(Ty, S.Context) && !isCFStringType(Ty, S.Context) && (!Ty->isPointerType() || @@ -1681,7 +1704,7 @@ static void HandleFormatArgAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) FormatArgAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) FormatArgAttr(Attr.getLoc(), S.Context, Idx.getZExtValue())); } @@ -1722,19 +1745,19 @@ static FormatAttrKind getFormatAttrKind(llvm::StringRef Format) { /// Handle __attribute__((init_priority(priority))) attributes based on /// http://gcc.gnu.org/onlinedocs/gcc/C_002b_002b-Attributes.html -static void HandleInitPriorityAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleInitPriorityAttr(Sema &S, Decl *D, + const AttributeList &Attr) { if (!S.getLangOptions().CPlusPlus) { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << Attr.getName(); return; } - if (!isa<VarDecl>(d) || S.getCurFunctionOrMethodDecl()) { + if (!isa<VarDecl>(D) || S.getCurFunctionOrMethodDecl()) { S.Diag(Attr.getLoc(), diag::err_init_priority_object_attr); Attr.setInvalid(); return; } - QualType T = dyn_cast<VarDecl>(d)->getType(); + QualType T = dyn_cast<VarDecl>(D)->getType(); if (S.Context.getAsArrayType(T)) T = S.Context.getBaseElementType(T); if (!T->getAs<RecordType>()) { @@ -1765,13 +1788,13 @@ static void HandleInitPriorityAttr(Decl *d, const AttributeList &Attr, Attr.setInvalid(); return; } - d->addAttr(::new (S.Context) InitPriorityAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) InitPriorityAttr(Attr.getLoc(), S.Context, prioritynum)); } /// Handle __attribute__((format(type,idx,firstarg))) attributes based on /// http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html -static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleFormatAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (!Attr.getParameterName()) { S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_string) @@ -1784,7 +1807,7 @@ static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - if (!isFunctionOrMethodOrBlock(d) || !hasFunctionProto(d)) { + if (!isFunctionOrMethodOrBlock(D) || !hasFunctionProto(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; @@ -1792,8 +1815,8 @@ static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { // In C++ the implicit 'this' function parameter also counts, and they are // counted from one. - bool HasImplicitThisParam = isInstanceMethod(d); - unsigned NumArgs = getFunctionOrMethodNumArgs(d) + HasImplicitThisParam; + bool HasImplicitThisParam = isInstanceMethod(D); + unsigned NumArgs = getFunctionOrMethodNumArgs(D) + HasImplicitThisParam; unsigned FirstIdx = 1; llvm::StringRef Format = Attr.getParameterName()->getName(); @@ -1844,7 +1867,7 @@ static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { } // make sure the format string is really a string - QualType Ty = getFunctionOrMethodArgType(d, ArgIdx); + QualType Ty = getFunctionOrMethodArgType(D, ArgIdx); if (Kind == CFStringFormat) { if (!isCFStringType(Ty, S.Context)) { @@ -1881,10 +1904,10 @@ static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { // check if the function is variadic if the 3rd argument non-zero if (FirstArg != 0) { - if (isFunctionOrMethodVariadic(d)) { + if (isFunctionOrMethodVariadic(D)) { ++NumArgs; // +1 for ... } else { - S.Diag(d->getLocation(), diag::err_format_attribute_requires_variadic); + S.Diag(D->getLocation(), diag::err_format_attribute_requires_variadic); return; } } @@ -1904,26 +1927,42 @@ static void HandleFormatAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) FormatAttr(Attr.getLoc(), S.Context, Format, + // Check whether we already have an equivalent format attribute. + for (specific_attr_iterator<FormatAttr> + i = D->specific_attr_begin<FormatAttr>(), + e = D->specific_attr_end<FormatAttr>(); + i != e ; ++i) { + FormatAttr *f = *i; + if (f->getType() == Format && + f->getFormatIdx() == (int)Idx.getZExtValue() && + f->getFirstArg() == (int)FirstArg.getZExtValue()) { + // If we don't have a valid location for this attribute, adopt the + // location. + if (f->getLocation().isInvalid()) + f->setLocation(Attr.getLoc()); + return; + } + } + + D->addAttr(::new (S.Context) FormatAttr(Attr.getLoc(), S.Context, Format, Idx.getZExtValue(), FirstArg.getZExtValue())); } -static void HandleTransparentUnionAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleTransparentUnionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } + // Try to find the underlying union declaration. RecordDecl *RD = 0; - TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(d); + TypedefNameDecl *TD = dyn_cast<TypedefNameDecl>(D); if (TD && TD->getUnderlyingType()->isUnionType()) RD = TD->getUnderlyingType()->getAsUnionType()->getDecl(); else - RD = dyn_cast<RecordDecl>(d); + RD = dyn_cast<RecordDecl>(D); if (!RD || !RD->isUnion()) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) @@ -1977,12 +2016,11 @@ static void HandleTransparentUnionAttr(Decl *d, const AttributeList &Attr, RD->addAttr(::new (S.Context) TransparentUnionAttr(Attr.getLoc(), S.Context)); } -static void HandleAnnotateAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleAnnotateAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + if (!checkAttributeNumArgs(S, Attr, 1)) return; - } + Expr *ArgExpr = Attr.getArg(0); StringLiteral *SE = dyn_cast<StringLiteral>(ArgExpr); @@ -1992,11 +2030,11 @@ static void HandleAnnotateAttr(Decl *d, const AttributeList &Attr, Sema &S) { S.Diag(ArgExpr->getLocStart(), diag::err_attribute_not_string) <<"annotate"; return; } - d->addAttr(::new (S.Context) AnnotateAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) AnnotateAttr(Attr.getLoc(), S.Context, SE->getString())); } -static void HandleAlignedAttr(Decl *D, const AttributeList &Attr, Sema &S) { +static void handleAlignedAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. if (Attr.getNumArgs() > 1) { S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; @@ -2045,21 +2083,20 @@ void Sema::AddAlignedAttr(SourceLocation AttrLoc, Decl *D, TypeSourceInfo *TS) { return; } -/// HandleModeAttr - This attribute modifies the width of a decl with primitive +/// handleModeAttr - This attribute modifies the width of a decl with primitive /// type. /// /// Despite what would be logical, the mode attribute is a decl attribute, not a /// type attribute: 'int ** __attribute((mode(HI))) *G;' tries to make 'G' be /// HImode, not an intermediate pointer. -static void HandleModeAttr(Decl *D, const AttributeList &Attr, Sema &S) { +static void handleModeAttr(Sema &S, Decl *D, const AttributeList &Attr) { // This attribute isn't documented, but glibc uses it. It changes // the width of an int or unsigned int to the specified size. // Check that there aren't any arguments - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } + IdentifierInfo *Name = Attr.getParameterName(); if (!Name) { @@ -2216,57 +2253,53 @@ static void HandleModeAttr(Decl *D, const AttributeList &Attr, Sema &S) { cast<ValueDecl>(D)->setType(NewTy); } -static void HandleNoDebugAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleNoDebugAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() > 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isFunctionOrMethod(d)) { + if (!isFunctionOrMethod(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) NoDebugAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) NoDebugAttr(Attr.getLoc(), S.Context)); } -static void HandleNoInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleNoInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isa<FunctionDecl>(d)) { + + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) NoInlineAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) NoInlineAttr(Attr.getLoc(), S.Context)); } -static void HandleNoInstrumentFunctionAttr(Decl *d, const AttributeList &Attr, - Sema &S) { +static void handleNoInstrumentFunctionAttr(Sema &S, Decl *D, + const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isa<FunctionDecl>(d)) { + + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) NoInstrumentFunctionAttr(Attr.getLoc(), + D->addAttr(::new (S.Context) NoInstrumentFunctionAttr(Attr.getLoc(), S.Context)); } -static void HandleConstantAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleConstantAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.CUDA) { // check the attribute arguments. if (Attr.hasParameterOrArguments()) { @@ -2274,19 +2307,19 @@ static void HandleConstantAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - if (!isa<VarDecl>(d)) { + if (!isa<VarDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariable; return; } - d->addAttr(::new (S.Context) CUDAConstantAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) CUDAConstantAttr(Attr.getLoc(), S.Context)); } else { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "constant"; } } -static void HandleDeviceAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleDeviceAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.CUDA) { // check the attribute arguments. if (Attr.getNumArgs() != 0) { @@ -2294,33 +2327,31 @@ static void HandleDeviceAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - if (!isa<FunctionDecl>(d) && !isa<VarDecl>(d)) { + if (!isa<FunctionDecl>(D) && !isa<VarDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariableOrFunction; return; } - d->addAttr(::new (S.Context) CUDADeviceAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) CUDADeviceAttr(Attr.getLoc(), S.Context)); } else { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "device"; } } -static void HandleGlobalAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleGlobalAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.CUDA) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isa<FunctionDecl>(d)) { + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - FunctionDecl *FD = cast<FunctionDecl>(d); + FunctionDecl *FD = cast<FunctionDecl>(D); if (!FD->getResultType()->isVoidType()) { TypeLoc TL = FD->getTypeSourceInfo()->getTypeLoc().IgnoreParens(); if (FunctionTypeLoc* FTL = dyn_cast<FunctionTypeLoc>(&TL)) { @@ -2335,60 +2366,56 @@ static void HandleGlobalAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) CUDAGlobalAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) CUDAGlobalAttr(Attr.getLoc(), S.Context)); } else { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "global"; } } -static void HandleHostAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleHostAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.CUDA) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isa<FunctionDecl>(d)) { + + if (!isa<FunctionDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; return; } - d->addAttr(::new (S.Context) CUDAHostAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) CUDAHostAttr(Attr.getLoc(), S.Context)); } else { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "host"; } } -static void HandleSharedAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleSharedAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.CUDA) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - if (!isa<VarDecl>(d)) { + + if (!isa<VarDecl>(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedVariable; return; } - d->addAttr(::new (S.Context) CUDASharedAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) CUDASharedAttr(Attr.getLoc(), S.Context)); } else { S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "shared"; } } -static void HandleGNUInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleGNUInlineAttr(Sema &S, Decl *D, const AttributeList &Attr) { // check the attribute arguments. - if (Attr.getNumArgs() != 0) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 0; + if (!checkAttributeNumArgs(S, Attr, 0)) return; - } - FunctionDecl *Fn = dyn_cast<FunctionDecl>(d); + FunctionDecl *Fn = dyn_cast<FunctionDecl>(D); if (Fn == 0) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunction; @@ -2400,47 +2427,47 @@ static void HandleGNUInlineAttr(Decl *d, const AttributeList &Attr, Sema &S) { return; } - d->addAttr(::new (S.Context) GNUInlineAttr(Attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) GNUInlineAttr(Attr.getLoc(), S.Context)); } -static void HandleCallConvAttr(Decl *d, const AttributeList &attr, Sema &S) { - if (hasDeclarator(d)) return; +static void handleCallConvAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (hasDeclarator(D)) return; - // Diagnostic is emitted elsewhere: here we store the (valid) attr + // Diagnostic is emitted elsewhere: here we store the (valid) Attr // in the Decl node for syntactic reasoning, e.g., pretty-printing. CallingConv CC; - if (S.CheckCallingConvAttr(attr, CC)) + if (S.CheckCallingConvAttr(Attr, CC)) return; - if (!isa<ObjCMethodDecl>(d)) { - S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << attr.getName() << ExpectedFunctionOrMethod; + if (!isa<ObjCMethodDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; return; } - switch (attr.getKind()) { + switch (Attr.getKind()) { case AttributeList::AT_fastcall: - d->addAttr(::new (S.Context) FastCallAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) FastCallAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_stdcall: - d->addAttr(::new (S.Context) StdCallAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) StdCallAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_thiscall: - d->addAttr(::new (S.Context) ThisCallAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) ThisCallAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_cdecl: - d->addAttr(::new (S.Context) CDeclAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) CDeclAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_pascal: - d->addAttr(::new (S.Context) PascalAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) PascalAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_pcs: { - Expr *Arg = attr.getArg(0); + Expr *Arg = Attr.getArg(0); StringLiteral *Str = dyn_cast<StringLiteral>(Arg); if (Str == 0 || Str->isWide()) { - S.Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string) + S.Diag(Attr.getLoc(), diag::err_attribute_argument_n_not_string) << "pcs" << 1; - attr.setInvalid(); + Attr.setInvalid(); return; } @@ -2451,12 +2478,12 @@ static void HandleCallConvAttr(Decl *d, const AttributeList &attr, Sema &S) { else if (StrRef == "aapcs-vfp") PCS = PcsAttr::AAPCS_VFP; else { - S.Diag(attr.getLoc(), diag::err_invalid_pcs); - attr.setInvalid(); + S.Diag(Attr.getLoc(), diag::err_invalid_pcs); + Attr.setInvalid(); return; } - d->addAttr(::new (S.Context) PcsAttr(attr.getLoc(), S.Context, PCS)); + D->addAttr(::new (S.Context) PcsAttr(Attr.getLoc(), S.Context, PCS)); } default: llvm_unreachable("unexpected attribute kind"); @@ -2464,9 +2491,9 @@ static void HandleCallConvAttr(Decl *d, const AttributeList &attr, Sema &S) { } } -static void HandleOpenCLKernelAttr(Decl *d, const AttributeList &Attr, Sema &S){ - assert(Attr.isInvalid() == false); - d->addAttr(::new (S.Context) OpenCLKernelAttr(Attr.getLoc(), S.Context)); +static void handleOpenCLKernelAttr(Sema &S, Decl *D, const AttributeList &Attr){ + assert(!Attr.isInvalid()); + D->addAttr(::new (S.Context) OpenCLKernelAttr(Attr.getLoc(), S.Context)); } bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { @@ -2515,63 +2542,63 @@ bool Sema::CheckCallingConvAttr(const AttributeList &attr, CallingConv &CC) { return false; } -static void HandleRegparmAttr(Decl *d, const AttributeList &attr, Sema &S) { - if (hasDeclarator(d)) return; +static void handleRegparmAttr(Sema &S, Decl *D, const AttributeList &Attr) { + if (hasDeclarator(D)) return; unsigned numParams; - if (S.CheckRegparmAttr(attr, numParams)) + if (S.CheckRegparmAttr(Attr, numParams)) return; - if (!isa<ObjCMethodDecl>(d)) { - S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) - << attr.getName() << ExpectedFunctionOrMethod; + if (!isa<ObjCMethodDecl>(D)) { + S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) + << Attr.getName() << ExpectedFunctionOrMethod; return; } - d->addAttr(::new (S.Context) RegparmAttr(attr.getLoc(), S.Context, numParams)); + D->addAttr(::new (S.Context) RegparmAttr(Attr.getLoc(), S.Context, numParams)); } /// Checks a regparm attribute, returning true if it is ill-formed and /// otherwise setting numParams to the appropriate value. -bool Sema::CheckRegparmAttr(const AttributeList &attr, unsigned &numParams) { - if (attr.isInvalid()) +bool Sema::CheckRegparmAttr(const AttributeList &Attr, unsigned &numParams) { + if (Attr.isInvalid()) return true; - if (attr.getNumArgs() != 1) { - Diag(attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; - attr.setInvalid(); + if (Attr.getNumArgs() != 1) { + Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + Attr.setInvalid(); return true; } - Expr *NumParamsExpr = attr.getArg(0); + Expr *NumParamsExpr = Attr.getArg(0); llvm::APSInt NumParams(32); if (NumParamsExpr->isTypeDependent() || NumParamsExpr->isValueDependent() || !NumParamsExpr->isIntegerConstantExpr(NumParams, Context)) { - Diag(attr.getLoc(), diag::err_attribute_argument_not_int) + Diag(Attr.getLoc(), diag::err_attribute_argument_not_int) << "regparm" << NumParamsExpr->getSourceRange(); - attr.setInvalid(); + Attr.setInvalid(); return true; } if (Context.Target.getRegParmMax() == 0) { - Diag(attr.getLoc(), diag::err_attribute_regparm_wrong_platform) + Diag(Attr.getLoc(), diag::err_attribute_regparm_wrong_platform) << NumParamsExpr->getSourceRange(); - attr.setInvalid(); + Attr.setInvalid(); return true; } numParams = NumParams.getZExtValue(); if (numParams > Context.Target.getRegParmMax()) { - Diag(attr.getLoc(), diag::err_attribute_regparm_invalid_number) + Diag(Attr.getLoc(), diag::err_attribute_regparm_invalid_number) << Context.Target.getRegParmMax() << NumParamsExpr->getSourceRange(); - attr.setInvalid(); + Attr.setInvalid(); return true; } return false; } -static void HandleLaunchBoundsAttr(Decl *d, const AttributeList &Attr, Sema &S){ +static void handleLaunchBoundsAttr(Sema &S, Decl *D, const AttributeList &Attr){ if (S.LangOpts.CUDA) { // check the attribute arguments. if (Attr.getNumArgs() != 1 && Attr.getNumArgs() != 2) { @@ -2580,7 +2607,7 @@ static void HandleLaunchBoundsAttr(Decl *d, const AttributeList &Attr, Sema &S){ return; } - if (!isFunctionOrMethod(d)) { + if (!isFunctionOrMethod(D)) { S.Diag(Attr.getLoc(), diag::warn_attribute_wrong_decl_type) << Attr.getName() << ExpectedFunctionOrMethod; return; @@ -2608,7 +2635,7 @@ static void HandleLaunchBoundsAttr(Decl *d, const AttributeList &Attr, Sema &S){ } } - d->addAttr(::new (S.Context) CUDALaunchBoundsAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) CUDALaunchBoundsAttr(Attr.getLoc(), S.Context, MaxThreads.getZExtValue(), MinBlocks.getZExtValue())); } else { @@ -2627,16 +2654,16 @@ static bool isValidSubjectOfCFAttribute(Sema &S, QualType type) { return type->isPointerType() || isValidSubjectOfNSAttribute(S, type); } -static void HandleNSConsumedAttr(Decl *d, const AttributeList &attr, Sema &S) { - ParmVarDecl *param = dyn_cast<ParmVarDecl>(d); +static void handleNSConsumedAttr(Sema &S, Decl *D, const AttributeList &Attr) { + ParmVarDecl *param = dyn_cast<ParmVarDecl>(D); if (!param) { - S.Diag(d->getLocStart(), diag::warn_attribute_wrong_decl_type) - << SourceRange(attr.getLoc()) << attr.getName() << ExpectedParameter; + S.Diag(D->getLocStart(), diag::warn_attribute_wrong_decl_type) + << SourceRange(Attr.getLoc()) << Attr.getName() << ExpectedParameter; return; } bool typeOK, cf; - if (attr.getKind() == AttributeList::AT_ns_consumed) { + if (Attr.getKind() == AttributeList::AT_ns_consumed) { typeOK = isValidSubjectOfNSAttribute(S, param->getType()); cf = false; } else { @@ -2645,47 +2672,52 @@ static void HandleNSConsumedAttr(Decl *d, const AttributeList &attr, Sema &S) { } if (!typeOK) { - S.Diag(d->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type) - << SourceRange(attr.getLoc()) << attr.getName() << cf; + S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_parameter_type) + << SourceRange(Attr.getLoc()) << Attr.getName() << cf; return; } if (cf) - param->addAttr(::new (S.Context) CFConsumedAttr(attr.getLoc(), S.Context)); + param->addAttr(::new (S.Context) CFConsumedAttr(Attr.getLoc(), S.Context)); else - param->addAttr(::new (S.Context) NSConsumedAttr(attr.getLoc(), S.Context)); + param->addAttr(::new (S.Context) NSConsumedAttr(Attr.getLoc(), S.Context)); } -static void HandleNSConsumesSelfAttr(Decl *d, const AttributeList &attr, - Sema &S) { - if (!isa<ObjCMethodDecl>(d)) { - S.Diag(d->getLocStart(), diag::warn_attribute_wrong_decl_type) - << SourceRange(attr.getLoc()) << attr.getName() << ExpectedMethod; +static void handleNSConsumesSelfAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!isa<ObjCMethodDecl>(D)) { + S.Diag(D->getLocStart(), diag::warn_attribute_wrong_decl_type) + << SourceRange(Attr.getLoc()) << Attr.getName() << ExpectedMethod; return; } - d->addAttr(::new (S.Context) NSConsumesSelfAttr(attr.getLoc(), S.Context)); + D->addAttr(::new (S.Context) NSConsumesSelfAttr(Attr.getLoc(), S.Context)); } -static void HandleNSReturnsRetainedAttr(Decl *d, const AttributeList &attr, - Sema &S) { +static void handleNSReturnsRetainedAttr(Sema &S, Decl *D, + const AttributeList &Attr) { QualType returnType; - if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(d)) + if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) returnType = MD->getResultType(); - else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(d)) + else if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(D)) + returnType = PD->getType(); + else if (S.getLangOptions().ObjCAutoRefCount && hasDeclarator(D) && + (Attr.getKind() == AttributeList::AT_ns_returns_retained)) + return; // ignore: was handled as a type attribute + else if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) returnType = FD->getResultType(); else { - S.Diag(d->getLocStart(), diag::warn_attribute_wrong_decl_type) - << SourceRange(attr.getLoc()) << attr.getName() + S.Diag(D->getLocStart(), diag::warn_attribute_wrong_decl_type) + << SourceRange(Attr.getLoc()) << Attr.getName() << ExpectedFunctionOrMethod; return; } bool typeOK; bool cf; - switch (attr.getKind()) { + switch (Attr.getKind()) { default: llvm_unreachable("invalid ownership attribute"); return; case AttributeList::AT_ns_returns_autoreleased: case AttributeList::AT_ns_returns_retained: @@ -2702,39 +2734,95 @@ static void HandleNSReturnsRetainedAttr(Decl *d, const AttributeList &attr, } if (!typeOK) { - S.Diag(d->getLocStart(), diag::warn_ns_attribute_wrong_return_type) - << SourceRange(attr.getLoc()) - << attr.getName() << isa<ObjCMethodDecl>(d) << cf; + S.Diag(D->getLocStart(), diag::warn_ns_attribute_wrong_return_type) + << SourceRange(Attr.getLoc()) + << Attr.getName() << isa<ObjCMethodDecl>(D) << cf; return; } - switch (attr.getKind()) { + switch (Attr.getKind()) { default: assert(0 && "invalid ownership attribute"); return; case AttributeList::AT_ns_returns_autoreleased: - d->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr(attr.getLoc(), + D->addAttr(::new (S.Context) NSReturnsAutoreleasedAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_cf_returns_not_retained: - d->addAttr(::new (S.Context) CFReturnsNotRetainedAttr(attr.getLoc(), + D->addAttr(::new (S.Context) CFReturnsNotRetainedAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_ns_returns_not_retained: - d->addAttr(::new (S.Context) NSReturnsNotRetainedAttr(attr.getLoc(), + D->addAttr(::new (S.Context) NSReturnsNotRetainedAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_cf_returns_retained: - d->addAttr(::new (S.Context) CFReturnsRetainedAttr(attr.getLoc(), + D->addAttr(::new (S.Context) CFReturnsRetainedAttr(Attr.getLoc(), S.Context)); return; case AttributeList::AT_ns_returns_retained: - d->addAttr(::new (S.Context) NSReturnsRetainedAttr(attr.getLoc(), + D->addAttr(::new (S.Context) NSReturnsRetainedAttr(Attr.getLoc(), S.Context)); return; }; } +static void handleObjCOwnershipAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (hasDeclarator(D)) return; + + SourceLocation L = Attr.getLoc(); + S.Diag(D->getLocStart(), diag::err_attribute_wrong_decl_type) + << SourceRange(L, L) << Attr.getName() << 12 /* variable */; +} + +static void handleObjCPreciseLifetimeAttr(Sema &S, Decl *D, + const AttributeList &Attr) { + if (!isa<VarDecl>(D) && !isa<FieldDecl>(D)) { + SourceLocation L = Attr.getLoc(); + S.Diag(D->getLocStart(), diag::err_attribute_wrong_decl_type) + << SourceRange(L, L) << Attr.getName() << 12 /* variable */; + return; + } + + ValueDecl *vd = cast<ValueDecl>(D); + QualType type = vd->getType(); + + if (!type->isDependentType() && + !type->isObjCLifetimeType()) { + S.Diag(Attr.getLoc(), diag::err_objc_precise_lifetime_bad_type) + << type; + return; + } + + Qualifiers::ObjCLifetime lifetime = type.getObjCLifetime(); + + // If we have no lifetime yet, check the lifetime we're presumably + // going to infer. + if (lifetime == Qualifiers::OCL_None && !type->isDependentType()) + lifetime = type->getObjCARCImplicitLifetime(); + + switch (lifetime) { + case Qualifiers::OCL_None: + assert(type->isDependentType() && + "didn't infer lifetime for non-dependent type?"); + break; + + case Qualifiers::OCL_Weak: // meaningful + case Qualifiers::OCL_Strong: // meaningful + break; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Autoreleasing: + S.Diag(Attr.getLoc(), diag::warn_objc_precise_lifetime_meaningless) + << (lifetime == Qualifiers::OCL_Autoreleasing); + break; + } + + D->addAttr(::new (S.Context) + ObjCPreciseLifetimeAttr(Attr.getLoc(), S.Context)); +} + static bool isKnownDeclSpecAttr(const AttributeList &Attr) { return Attr.getKind() == AttributeList::AT_dllimport || Attr.getKind() == AttributeList::AT_dllexport || @@ -2745,13 +2833,12 @@ static bool isKnownDeclSpecAttr(const AttributeList &Attr) { // Microsoft specific attribute handlers. //===----------------------------------------------------------------------===// -static void HandleUuidAttr(Decl *d, const AttributeList &Attr, Sema &S) { +static void handleUuidAttr(Sema &S, Decl *D, const AttributeList &Attr) { if (S.LangOpts.Microsoft || S.LangOpts.Borland) { // check the attribute arguments. - if (Attr.getNumArgs() != 1) { - S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + if (!checkAttributeNumArgs(S, Attr, 1)) return; - } + Expr *Arg = Attr.getArg(0); StringLiteral *Str = dyn_cast<StringLiteral>(Arg); if (Str == 0 || Str->isWide()) { @@ -2794,7 +2881,7 @@ static void HandleUuidAttr(Decl *d, const AttributeList &Attr, Sema &S) { I++; } - d->addAttr(::new (S.Context) UuidAttr(Attr.getLoc(), S.Context, + D->addAttr(::new (S.Context) UuidAttr(Attr.getLoc(), S.Context, Str->getString())); } else S.Diag(Attr.getLoc(), diag::warn_attribute_ignored) << "uuid"; @@ -2804,24 +2891,24 @@ static void HandleUuidAttr(Decl *d, const AttributeList &Attr, Sema &S) { // Top Level Sema Entry Points //===----------------------------------------------------------------------===// -static void ProcessNonInheritableDeclAttr(Scope *scope, Decl *D, - const AttributeList &Attr, Sema &S) { +static void ProcessNonInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, + const AttributeList &Attr) { switch (Attr.getKind()) { - case AttributeList::AT_device: HandleDeviceAttr (D, Attr, S); break; - case AttributeList::AT_host: HandleHostAttr (D, Attr, S); break; - case AttributeList::AT_overloadable:HandleOverloadableAttr(D, Attr, S); break; + case AttributeList::AT_device: handleDeviceAttr (S, D, Attr); break; + case AttributeList::AT_host: handleHostAttr (S, D, Attr); break; + case AttributeList::AT_overloadable:handleOverloadableAttr(S, D, Attr); break; default: break; } } -static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, - const AttributeList &Attr, Sema &S) { +static void ProcessInheritableDeclAttr(Sema &S, Scope *scope, Decl *D, + const AttributeList &Attr) { switch (Attr.getKind()) { - case AttributeList::AT_IBAction: HandleIBAction(D, Attr, S); break; - case AttributeList::AT_IBOutlet: HandleIBOutlet(D, Attr, S); break; + case AttributeList::AT_IBAction: handleIBAction(S, D, Attr); break; + case AttributeList::AT_IBOutlet: handleIBOutlet(S, D, Attr); break; case AttributeList::AT_IBOutletCollection: - HandleIBOutletCollection(D, Attr, S); break; + handleIBOutletCollection(S, D, Attr); break; case AttributeList::AT_address_space: case AttributeList::AT_opencl_image_access: case AttributeList::AT_objc_gc: @@ -2837,100 +2924,108 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, // Ignore, this is a non-inheritable attribute, handled // by ProcessNonInheritableDeclAttr. break; - case AttributeList::AT_alias: HandleAliasAttr (D, Attr, S); break; - case AttributeList::AT_aligned: HandleAlignedAttr (D, Attr, S); break; + case AttributeList::AT_alias: handleAliasAttr (S, D, Attr); break; + case AttributeList::AT_aligned: handleAlignedAttr (S, D, Attr); break; case AttributeList::AT_always_inline: - HandleAlwaysInlineAttr (D, Attr, S); break; + handleAlwaysInlineAttr (S, D, Attr); break; case AttributeList::AT_analyzer_noreturn: - HandleAnalyzerNoReturnAttr (D, Attr, S); break; - case AttributeList::AT_annotate: HandleAnnotateAttr (D, Attr, S); break; - case AttributeList::AT_availability:HandleAvailabilityAttr(D, Attr, S); break; + handleAnalyzerNoReturnAttr (S, D, Attr); break; + case AttributeList::AT_annotate: handleAnnotateAttr (S, D, Attr); break; + case AttributeList::AT_availability:handleAvailabilityAttr(S, D, Attr); break; case AttributeList::AT_carries_dependency: - HandleDependencyAttr (D, Attr, S); break; - case AttributeList::AT_common: HandleCommonAttr (D, Attr, S); break; - case AttributeList::AT_constant: HandleConstantAttr (D, Attr, S); break; - case AttributeList::AT_constructor: HandleConstructorAttr (D, Attr, S); break; - case AttributeList::AT_deprecated: HandleDeprecatedAttr (D, Attr, S); break; - case AttributeList::AT_destructor: HandleDestructorAttr (D, Attr, S); break; + handleDependencyAttr (S, D, Attr); break; + case AttributeList::AT_common: handleCommonAttr (S, D, Attr); break; + case AttributeList::AT_constant: handleConstantAttr (S, D, Attr); break; + case AttributeList::AT_constructor: handleConstructorAttr (S, D, Attr); break; + case AttributeList::AT_deprecated: handleDeprecatedAttr (S, D, Attr); break; + case AttributeList::AT_destructor: handleDestructorAttr (S, D, Attr); break; case AttributeList::AT_ext_vector_type: - HandleExtVectorTypeAttr(scope, D, Attr, S); + handleExtVectorTypeAttr(S, scope, D, Attr); break; - case AttributeList::AT_format: HandleFormatAttr (D, Attr, S); break; - case AttributeList::AT_format_arg: HandleFormatArgAttr (D, Attr, S); break; - case AttributeList::AT_global: HandleGlobalAttr (D, Attr, S); break; - case AttributeList::AT_gnu_inline: HandleGNUInlineAttr (D, Attr, S); break; + case AttributeList::AT_format: handleFormatAttr (S, D, Attr); break; + case AttributeList::AT_format_arg: handleFormatArgAttr (S, D, Attr); break; + case AttributeList::AT_global: handleGlobalAttr (S, D, Attr); break; + case AttributeList::AT_gnu_inline: handleGNUInlineAttr (S, D, Attr); break; case AttributeList::AT_launch_bounds: - HandleLaunchBoundsAttr(D, Attr, S); + handleLaunchBoundsAttr(S, D, Attr); break; - case AttributeList::AT_mode: HandleModeAttr (D, Attr, S); break; - case AttributeList::AT_malloc: HandleMallocAttr (D, Attr, S); break; - case AttributeList::AT_may_alias: HandleMayAliasAttr (D, Attr, S); break; - case AttributeList::AT_nocommon: HandleNoCommonAttr (D, Attr, S); break; - case AttributeList::AT_nonnull: HandleNonNullAttr (D, Attr, S); break; + case AttributeList::AT_mode: handleModeAttr (S, D, Attr); break; + case AttributeList::AT_malloc: handleMallocAttr (S, D, Attr); break; + case AttributeList::AT_may_alias: handleMayAliasAttr (S, D, Attr); break; + case AttributeList::AT_nocommon: handleNoCommonAttr (S, D, Attr); break; + case AttributeList::AT_nonnull: handleNonNullAttr (S, D, Attr); break; case AttributeList::AT_ownership_returns: case AttributeList::AT_ownership_takes: case AttributeList::AT_ownership_holds: - HandleOwnershipAttr (D, Attr, S); break; - case AttributeList::AT_naked: HandleNakedAttr (D, Attr, S); break; - case AttributeList::AT_noreturn: HandleNoReturnAttr (D, Attr, S); break; - case AttributeList::AT_nothrow: HandleNothrowAttr (D, Attr, S); break; - case AttributeList::AT_shared: HandleSharedAttr (D, Attr, S); break; - case AttributeList::AT_vecreturn: HandleVecReturnAttr (D, Attr, S); break; + handleOwnershipAttr (S, D, Attr); break; + case AttributeList::AT_naked: handleNakedAttr (S, D, Attr); break; + case AttributeList::AT_noreturn: handleNoReturnAttr (S, D, Attr); break; + case AttributeList::AT_nothrow: handleNothrowAttr (S, D, Attr); break; + case AttributeList::AT_shared: handleSharedAttr (S, D, Attr); break; + case AttributeList::AT_vecreturn: handleVecReturnAttr (S, D, Attr); break; + + case AttributeList::AT_objc_ownership: + handleObjCOwnershipAttr(S, D, Attr); break; + case AttributeList::AT_objc_precise_lifetime: + handleObjCPreciseLifetimeAttr(S, D, Attr); break; // Checker-specific. case AttributeList::AT_cf_consumed: - case AttributeList::AT_ns_consumed: HandleNSConsumedAttr (D, Attr, S); break; + case AttributeList::AT_ns_consumed: handleNSConsumedAttr (S, D, Attr); break; case AttributeList::AT_ns_consumes_self: - HandleNSConsumesSelfAttr(D, Attr, S); break; + handleNSConsumesSelfAttr(S, D, Attr); break; case AttributeList::AT_ns_returns_autoreleased: case AttributeList::AT_ns_returns_not_retained: case AttributeList::AT_cf_returns_not_retained: case AttributeList::AT_ns_returns_retained: case AttributeList::AT_cf_returns_retained: - HandleNSReturnsRetainedAttr(D, Attr, S); break; + handleNSReturnsRetainedAttr(S, D, Attr); break; case AttributeList::AT_reqd_wg_size: - HandleReqdWorkGroupSize(D, Attr, S); break; + handleReqdWorkGroupSize(S, D, Attr); break; case AttributeList::AT_init_priority: - HandleInitPriorityAttr(D, Attr, S); break; + handleInitPriorityAttr(S, D, Attr); break; - case AttributeList::AT_packed: HandlePackedAttr (D, Attr, S); break; - case AttributeList::AT_MsStruct: HandleMsStructAttr (D, Attr, S); break; - case AttributeList::AT_section: HandleSectionAttr (D, Attr, S); break; - case AttributeList::AT_unavailable: HandleUnavailableAttr (D, Attr, S); break; - case AttributeList::AT_unused: HandleUnusedAttr (D, Attr, S); break; - case AttributeList::AT_used: HandleUsedAttr (D, Attr, S); break; - case AttributeList::AT_visibility: HandleVisibilityAttr (D, Attr, S); break; - case AttributeList::AT_warn_unused_result: HandleWarnUnusedResult(D,Attr,S); + case AttributeList::AT_packed: handlePackedAttr (S, D, Attr); break; + case AttributeList::AT_MsStruct: handleMsStructAttr (S, D, Attr); break; + case AttributeList::AT_section: handleSectionAttr (S, D, Attr); break; + case AttributeList::AT_unavailable: handleUnavailableAttr (S, D, Attr); break; + case AttributeList::AT_arc_weakref_unavailable: + handleArcWeakrefUnavailableAttr (S, D, Attr); break; - case AttributeList::AT_weak: HandleWeakAttr (D, Attr, S); break; - case AttributeList::AT_weakref: HandleWeakRefAttr (D, Attr, S); break; - case AttributeList::AT_weak_import: HandleWeakImportAttr (D, Attr, S); break; + case AttributeList::AT_unused: handleUnusedAttr (S, D, Attr); break; + case AttributeList::AT_used: handleUsedAttr (S, D, Attr); break; + case AttributeList::AT_visibility: handleVisibilityAttr (S, D, Attr); break; + case AttributeList::AT_warn_unused_result: handleWarnUnusedResult(S, D, Attr); + break; + case AttributeList::AT_weak: handleWeakAttr (S, D, Attr); break; + case AttributeList::AT_weakref: handleWeakRefAttr (S, D, Attr); break; + case AttributeList::AT_weak_import: handleWeakImportAttr (S, D, Attr); break; case AttributeList::AT_transparent_union: - HandleTransparentUnionAttr(D, Attr, S); + handleTransparentUnionAttr(S, D, Attr); break; case AttributeList::AT_objc_exception: - HandleObjCExceptionAttr(D, Attr, S); + handleObjCExceptionAttr(S, D, Attr); break; case AttributeList::AT_objc_method_family: - HandleObjCMethodFamilyAttr(D, Attr, S); + handleObjCMethodFamilyAttr(S, D, Attr); break; - case AttributeList::AT_nsobject: HandleObjCNSObject (D, Attr, S); break; - case AttributeList::AT_blocks: HandleBlocksAttr (D, Attr, S); break; - case AttributeList::AT_sentinel: HandleSentinelAttr (D, Attr, S); break; - case AttributeList::AT_const: HandleConstAttr (D, Attr, S); break; - case AttributeList::AT_pure: HandlePureAttr (D, Attr, S); break; - case AttributeList::AT_cleanup: HandleCleanupAttr (D, Attr, S); break; - case AttributeList::AT_nodebug: HandleNoDebugAttr (D, Attr, S); break; - case AttributeList::AT_noinline: HandleNoInlineAttr (D, Attr, S); break; - case AttributeList::AT_regparm: HandleRegparmAttr (D, Attr, S); break; + case AttributeList::AT_nsobject: handleObjCNSObject (S, D, Attr); break; + case AttributeList::AT_blocks: handleBlocksAttr (S, D, Attr); break; + case AttributeList::AT_sentinel: handleSentinelAttr (S, D, Attr); break; + case AttributeList::AT_const: handleConstAttr (S, D, Attr); break; + case AttributeList::AT_pure: handlePureAttr (S, D, Attr); break; + case AttributeList::AT_cleanup: handleCleanupAttr (S, D, Attr); break; + case AttributeList::AT_nodebug: handleNoDebugAttr (S, D, Attr); break; + case AttributeList::AT_noinline: handleNoInlineAttr (S, D, Attr); break; + case AttributeList::AT_regparm: handleRegparmAttr (S, D, Attr); break; case AttributeList::IgnoredAttribute: // Just ignore break; case AttributeList::AT_no_instrument_function: // Interacts with -pg. - HandleNoInstrumentFunctionAttr(D, Attr, S); + handleNoInstrumentFunctionAttr(S, D, Attr); break; case AttributeList::AT_stdcall: case AttributeList::AT_cdecl: @@ -2938,13 +3033,13 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, case AttributeList::AT_thiscall: case AttributeList::AT_pascal: case AttributeList::AT_pcs: - HandleCallConvAttr(D, Attr, S); + handleCallConvAttr(S, D, Attr); break; case AttributeList::AT_opencl_kernel_function: - HandleOpenCLKernelAttr(D, Attr, S); + handleOpenCLKernelAttr(S, D, Attr); break; case AttributeList::AT_uuid: - HandleUuidAttr(D, Attr, S); + handleUuidAttr(S, D, Attr); break; default: // Ask target about the attribute. @@ -2960,8 +3055,8 @@ static void ProcessInheritableDeclAttr(Scope *scope, Decl *D, /// the attribute applies to decls. If the attribute is a type attribute, just /// silently ignore it if a GNU attribute. FIXME: Applying a C++0x attribute to /// the wrong thing is illegal (C++0x [dcl.attr.grammar]/4). -static void ProcessDeclAttribute(Scope *scope, Decl *D, - const AttributeList &Attr, Sema &S, +static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, + const AttributeList &Attr, bool NonInheritable, bool Inheritable) { if (Attr.isInvalid()) return; @@ -2971,10 +3066,10 @@ static void ProcessDeclAttribute(Scope *scope, Decl *D, return; if (NonInheritable) - ProcessNonInheritableDeclAttr(scope, D, Attr, S); + ProcessNonInheritableDeclAttr(S, scope, D, Attr); if (Inheritable) - ProcessInheritableDeclAttr(scope, D, Attr, S); + ProcessInheritableDeclAttr(S, scope, D, Attr); } /// ProcessDeclAttributeList - Apply all the decl attributes in the specified @@ -2983,7 +3078,7 @@ void Sema::ProcessDeclAttributeList(Scope *S, Decl *D, const AttributeList *AttrList, bool NonInheritable, bool Inheritable) { for (const AttributeList* l = AttrList; l; l = l->getNext()) { - ProcessDeclAttribute(S, D, *l, *this, NonInheritable, Inheritable); + ProcessDeclAttribute(*this, S, D, *l, NonInheritable, Inheritable); } // GCC accepts @@ -3085,6 +3180,32 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD, ProcessDeclAttributeList(S, D, Attrs, NonInheritable, Inheritable); } +/// Is the given declaration allowed to use a forbidden type? +static bool isForbiddenTypeAllowed(Sema &S, Decl *decl) { + // Private ivars are always okay. Unfortunately, people don't + // always properly make their ivars private, even in system headers. + // Plus we need to make fields okay, too. + if (!isa<FieldDecl>(decl) && !isa<ObjCPropertyDecl>(decl)) + return false; + + // Require it to be declared in a system header. + return S.Context.getSourceManager().isInSystemHeader(decl->getLocation()); +} + +/// Handle a delayed forbidden-type diagnostic. +static void handleDelayedForbiddenType(Sema &S, DelayedDiagnostic &diag, + Decl *decl) { + if (decl && isForbiddenTypeAllowed(S, decl)) { + decl->addAttr(new (S.Context) UnavailableAttr(diag.Loc, S.Context, + "this system declaration uses an unsupported type")); + return; + } + + S.Diag(diag.Loc, diag.getForbiddenTypeDiagnostic()) + << diag.getForbiddenTypeOperand() << diag.getForbiddenTypeArgument(); + diag.Triggered = true; +} + // This duplicates a vector push_back but hides the need to know the // size of the type. void Sema::DelayedDiagnostics::add(const DelayedDiagnostic &diag) { @@ -3126,7 +3247,7 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state, // We only want to actually emit delayed diagnostics when we // successfully parsed a decl. - if (decl) { + if (decl && !decl->isInvalidDecl()) { // We emit all the active diagnostics, not just those starting // from the saved state. The idea is this: we get one push for a // decl spec and another for each declarator; in a decl group like: @@ -3147,6 +3268,10 @@ void Sema::DelayedDiagnostics::popParsingDecl(Sema &S, ParsingDeclState state, case DelayedDiagnostic::Access: S.HandleDelayedAccessCheck(diag, decl); break; + + case DelayedDiagnostic::ForbiddenType: + handleDelayedForbiddenType(S, diag, decl); + break; } } } diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index ce99efbd0bd2..d793daf9d826 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -185,9 +185,9 @@ void Sema::ImplicitExceptionSpecification::CalledExpr(Expr *E) { // FIXME: // // C++0x [except.spec]p14: - // [An] implicit exception-specification specifies the type-id T if and - // only if T is allowed by the exception-specification of a function directly - // invoked by f’s implicit definition; f shall allow all exceptions if any + // [An] implicit exception-specification specifies the type-id T if and + // only if T is allowed by the exception-specification of a function directly + // invoked by f's implicit definition; f shall allow all exceptions if any // function it directly invokes allows all exceptions, and f shall allow no // exceptions if every function it directly invokes allows no exceptions. // @@ -762,13 +762,6 @@ bool Sema::AttachBaseSpecifiers(CXXRecordDecl *Class, CXXBaseSpecifier **Bases, QualType NewBaseType = Context.getCanonicalType(Bases[idx]->getType()); NewBaseType = NewBaseType.getLocalUnqualifiedType(); - if (!Class->hasObjectMember()) { - if (const RecordType *FDTTy = - NewBaseType.getTypePtr()->getAs<RecordType>()) - if (FDTTy->getDecl()->hasObjectMember()) - Class->setHasObjectMember(true); - } - if (KnownBaseTypes[NewBaseType]) { // C++ [class.mi]p3: // A class shall not be specified as a direct base class of a @@ -1086,14 +1079,7 @@ Sema::ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS, Declarator &D, assert(!DS.isFriendSpecified()); assert(!Init || !HasDeferredInit); - bool isFunc = false; - if (D.isFunctionDeclarator()) - isFunc = true; - else if (D.getNumTypeObjects() == 0 && - D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_typename) { - QualType TDType = GetTypeFromParser(DS.getRepAsType()); - isFunc = TDType->isFunctionType(); - } + bool isFunc = D.isDeclarationOfFunction(); // C++ 9.2p6: A member shall not be declared to have automatic storage // duration (auto, register) or with the extern storage-class-specifier. @@ -1440,25 +1426,27 @@ Sema::ActOnMemInitializer(Decl *ConstructorD, } // If no results were found, try to correct typos. + TypoCorrection Corr; if (R.empty() && BaseType.isNull() && - CorrectTypo(R, S, &SS, ClassDecl, 0, CTC_NoKeywords) && - R.isSingleResult()) { - if (FieldDecl *Member = R.getAsSingle<FieldDecl>()) { + (Corr = CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), S, &SS, + ClassDecl, false, CTC_NoKeywords))) { + std::string CorrectedStr(Corr.getAsString(getLangOptions())); + std::string CorrectedQuotedStr(Corr.getQuoted(getLangOptions())); + if (FieldDecl *Member = Corr.getCorrectionDeclAs<FieldDecl>()) { if (Member->getDeclContext()->getRedeclContext()->Equals(ClassDecl)) { // We have found a non-static data member with a similar // name to what was typed; complain and initialize that // member. Diag(R.getNameLoc(), diag::err_mem_init_not_member_or_class_suggest) - << MemberOrBase << true << R.getLookupName() - << FixItHint::CreateReplacement(R.getNameLoc(), - R.getLookupName().getAsString()); + << MemberOrBase << true << CorrectedQuotedStr + << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); Diag(Member->getLocation(), diag::note_previous_decl) - << Member->getDeclName(); + << CorrectedQuotedStr; return BuildMemberInitializer(Member, (Expr**)Args, NumArgs, IdLoc, LParenLoc, RParenLoc); } - } else if (TypeDecl *Type = R.getAsSingle<TypeDecl>()) { + } else if (TypeDecl *Type = Corr.getCorrectionDeclAs<TypeDecl>()) { const CXXBaseSpecifier *DirectBaseSpec; const CXXBaseSpecifier *VirtualBaseSpec; if (FindBaseInitializer(*this, ClassDecl, @@ -1468,9 +1456,8 @@ Sema::ActOnMemInitializer(Decl *ConstructorD, // similar name to what was typed; complain and initialize // that base class. Diag(R.getNameLoc(), diag::err_mem_init_not_member_or_class_suggest) - << MemberOrBase << false << R.getLookupName() - << FixItHint::CreateReplacement(R.getNameLoc(), - R.getLookupName().getAsString()); + << MemberOrBase << false << CorrectedQuotedStr + << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); const CXXBaseSpecifier *BaseSpec = DirectBaseSpec? DirectBaseSpec : VirtualBaseSpec; @@ -1617,14 +1604,10 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr **Args, // Can't check initialization for a member of dependent type or when // any of the arguments are type-dependent expressions. Init = new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, - RParenLoc); - - // Erase any temporaries within this evaluation context; we're not - // going to track them in the AST, since we'll be rebuilding the - // ASTs during template instantiation. - ExprTemporaries.erase( - ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, - ExprTemporaries.end()); + RParenLoc, + Member->getType().getNonReferenceType()); + + DiscardCleanupsInEvaluationContext(); } else { // Initialize the member. InitializedEntity MemberEntity = @@ -1658,8 +1641,9 @@ Sema::BuildMemberInitializer(ValueDecl *Member, Expr **Args, // initializer. However, deconstructing the ASTs is a dicey process, // and this approach is far more likely to get the corner cases right. if (CurContext->isDependentContext()) - Init = new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, - RParenLoc); + Init = new (Context) ParenListExpr( + Context, LParenLoc, Args, NumArgs, RParenLoc, + Member->getType().getNonReferenceType()); else Init = MemberInit.get(); } @@ -1715,22 +1699,7 @@ Sema::BuildDelegatingInitializer(TypeSourceInfo *TInfo, if (DelegationInit.isInvalid()) return true; - // If we are in a dependent context, template instantiation will - // perform this type-checking again. Just save the arguments that we - // received in a ParenListExpr. - // FIXME: This isn't quite ideal, since our ASTs don't capture all - // of the information that we have about the base - // initializer. However, deconstructing the ASTs is a dicey process, - // and this approach is far more likely to get the corner cases right. - if (CurContext->isDependentContext()) { - ExprResult Init - = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, - NumArgs, RParenLoc)); - return new (Context) CXXCtorInitializer(Context, Loc, LParenLoc, - Constructor, Init.takeAs<Expr>(), - RParenLoc); - } - + assert(!CurContext->isDependentContext()); return new (Context) CXXCtorInitializer(Context, Loc, LParenLoc, Constructor, DelegationInit.takeAs<Expr>(), RParenLoc); @@ -1815,14 +1784,9 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, // any of the arguments are type-dependent expressions. ExprResult BaseInit = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, - RParenLoc)); + RParenLoc, BaseType)); - // Erase any temporaries within this evaluation context; we're not - // going to track them in the AST, since we'll be rebuilding the - // ASTs during template instantiation. - ExprTemporaries.erase( - ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, - ExprTemporaries.end()); + DiscardCleanupsInEvaluationContext(); return new (Context) CXXCtorInitializer(Context, BaseTInfo, /*IsVirtual=*/false, @@ -1878,7 +1842,7 @@ Sema::BuildBaseInitializer(QualType BaseType, TypeSourceInfo *BaseTInfo, if (CurContext->isDependentContext()) { ExprResult Init = Owned(new (Context) ParenListExpr(Context, LParenLoc, Args, NumArgs, - RParenLoc)); + RParenLoc, BaseType)); return new (Context) CXXCtorInitializer(Context, BaseTInfo, BaseSpec->isVirtual(), LParenLoc, @@ -1989,6 +1953,11 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, if (ImplicitInitKind == IIK_Copy) { ParmVarDecl *Param = Constructor->getParamDecl(0); QualType ParamType = Param->getType().getNonReferenceType(); + + // Suppress copying zero-width bitfields. + if (const Expr *Width = Field->getBitWidth()) + if (Width->EvaluateAsInt(SemaRef.Context) == 0) + return false; Expr *MemberExprBase = DeclRefExpr::Create(SemaRef.Context, NestedNameSpecifierLoc(), Param, @@ -2134,6 +2103,20 @@ BuildImplicitMemberInitializer(Sema &SemaRef, CXXConstructorDecl *Constructor, } } + if (SemaRef.getLangOptions().ObjCAutoRefCount && + FieldBaseElementType->isObjCRetainableType() && + FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_None && + FieldBaseElementType.getObjCLifetime() != Qualifiers::OCL_ExplicitNone) { + // Instant objects: + // Default-initialize Objective-C pointers to NULL. + CXXMemberInit + = new (SemaRef.Context) CXXCtorInitializer(SemaRef.Context, Field, + Loc, Loc, + new (SemaRef.Context) ImplicitValueInitExpr(Field->getType()), + Loc); + return false; + } + // Nothing to initialize. CXXMemberInit = 0; return false; @@ -2207,11 +2190,8 @@ static bool CollectFieldInitializer(Sema &SemaRef, BaseAndFieldInfo &Info, } } - // Fallthrough and construct a default initializer for the union as - // a whole, which can call its default constructor if such a thing exists - // (C++0x perhaps). FIXME: It's not clear that this is the correct - // behavior going forward with C++0x, when anonymous unions there are - // finalized, we should revisit this. + // FIXME: C++0x unrestricted unions might call a default constructor here. + return false; } else { // For structs, we simply descend through to initialize all members where // necessary. @@ -2259,11 +2239,10 @@ Sema::SetDelegatingInitializer(CXXConstructorDecl *Constructor, return false; } -bool -Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, - CXXCtorInitializer **Initializers, - unsigned NumInitializers, - bool AnyErrors) { +bool Sema::SetCtorInitializers(CXXConstructorDecl *Constructor, + CXXCtorInitializer **Initializers, + unsigned NumInitializers, + bool AnyErrors) { if (Constructor->getDeclContext()->isDependentContext()) { // Just store the initializers as written, they will be checked during // instantiation. @@ -3405,7 +3384,7 @@ void Sema::CheckExplicitlyDefaultedDestructor(CXXDestructorDecl *DD) { bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { CXXRecordDecl *RD = CD->getParent(); assert(!RD->isDependentType() && "do deletion after instantiation"); - if (!LangOpts.CPlusPlus0x) + if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) return false; SourceLocation Loc = CD->getLocation(); @@ -3586,7 +3565,7 @@ bool Sema::ShouldDeleteDefaultConstructor(CXXConstructorDecl *CD) { bool Sema::ShouldDeleteCopyConstructor(CXXConstructorDecl *CD) { CXXRecordDecl *RD = CD->getParent(); assert(!RD->isDependentType() && "do deletion after instantiation"); - if (!LangOpts.CPlusPlus0x) + if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) return false; SourceLocation Loc = CD->getLocation(); @@ -3636,7 +3615,7 @@ bool Sema::ShouldDeleteCopyConstructor(CXXConstructorDecl *CD) { // resolution, as applied to B's [copy] constructor, results in an // ambiguity or a function that is deleted or inaccessible from the // defaulted constructor - CXXConstructorDecl *BaseCtor = LookupCopyConstructor(BaseDecl, ArgQuals); + CXXConstructorDecl *BaseCtor = LookupCopyingConstructor(BaseDecl, ArgQuals); if (!BaseCtor || BaseCtor->isDeleted()) return true; if (CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(), PDiag()) != @@ -3664,7 +3643,7 @@ bool Sema::ShouldDeleteCopyConstructor(CXXConstructorDecl *CD) { // resolution, as applied to B's [copy] constructor, results in an // ambiguity or a function that is deleted or inaccessible from the // defaulted constructor - CXXConstructorDecl *BaseCtor = LookupCopyConstructor(BaseDecl, ArgQuals); + CXXConstructorDecl *BaseCtor = LookupCopyingConstructor(BaseDecl, ArgQuals); if (!BaseCtor || BaseCtor->isDeleted()) return true; if (CheckConstructorAccess(Loc, BaseCtor, BaseCtor->getAccess(), PDiag()) != @@ -3726,8 +3705,8 @@ bool Sema::ShouldDeleteCopyConstructor(CXXConstructorDecl *CD) { // cannot be [copied] because overload resolution, as applied to B's // [copy] constructor, results in an ambiguity or a function that is // deleted or inaccessible from the defaulted constructor - CXXConstructorDecl *FieldCtor = LookupCopyConstructor(FieldRecord, - ArgQuals); + CXXConstructorDecl *FieldCtor = LookupCopyingConstructor(FieldRecord, + ArgQuals); if (!FieldCtor || FieldCtor->isDeleted()) return true; if (CheckConstructorAccess(Loc, FieldCtor, FieldCtor->getAccess(), @@ -3742,7 +3721,7 @@ bool Sema::ShouldDeleteCopyConstructor(CXXConstructorDecl *CD) { bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { CXXRecordDecl *RD = MD->getParent(); assert(!RD->isDependentType() && "do deletion after instantiation"); - if (!LangOpts.CPlusPlus0x) + if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) return false; SourceLocation Loc = MD->getLocation(); @@ -3752,19 +3731,15 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { bool Union = RD->isUnion(); - bool ConstArg = - MD->getParamDecl(0)->getType()->getPointeeType().isConstQualified(); + unsigned ArgQuals = + MD->getParamDecl(0)->getType()->getPointeeType().isConstQualified() ? + Qualifiers::Const : 0; // We do this because we should never actually use an anonymous // union's constructor. if (Union && RD->isAnonymousStructOrUnion()) return false; - DeclarationName OperatorName = - Context.DeclarationNames.getCXXOperatorName(OO_Equal); - LookupResult R(*this, OperatorName, Loc, LookupOrdinaryName); - R.suppressDiagnostics(); - // FIXME: We should put some diagnostic logic right into this function. // C++0x [class.copy]/11 @@ -3786,37 +3761,11 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { // resolution, as applied to B's [copy] assignment operator, results in // an ambiguity or a function that is deleted or inaccessible from the // assignment operator - - LookupQualifiedName(R, BaseDecl, false); - - // Filter out any result that isn't a copy-assignment operator. - LookupResult::Filter F = R.makeFilter(); - while (F.hasNext()) { - NamedDecl *D = F.next(); - if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) - if (Method->isCopyAssignmentOperator()) - continue; - - F.erase(); - } - F.done(); - - // Build a fake argument expression - QualType ArgType = BaseType; - QualType ThisType = BaseType; - if (ConstArg) - ArgType.addConst(); - Expr *Args[] = { new (Context) OpaqueValueExpr(Loc, ThisType, VK_LValue) - , new (Context) OpaqueValueExpr(Loc, ArgType, VK_LValue) - }; - - OverloadCandidateSet OCS((Loc)); - OverloadCandidateSet::iterator Best; - - AddFunctionCandidates(R.asUnresolvedSet(), Args, 2, OCS); - - if (OCS.BestViableFunction(*this, Loc, Best, false) != - OR_Success) + CXXMethodDecl *CopyOper = LookupCopyingAssignment(BaseDecl, ArgQuals, false, + 0); + if (!CopyOper || CopyOper->isDeleted()) + return true; + if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible) return true; } @@ -3831,37 +3780,11 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { // resolution, as applied to B's [copy] assignment operator, results in // an ambiguity or a function that is deleted or inaccessible from the // assignment operator - - LookupQualifiedName(R, BaseDecl, false); - - // Filter out any result that isn't a copy-assignment operator. - LookupResult::Filter F = R.makeFilter(); - while (F.hasNext()) { - NamedDecl *D = F.next(); - if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) - if (Method->isCopyAssignmentOperator()) - continue; - - F.erase(); - } - F.done(); - - // Build a fake argument expression - QualType ArgType = BaseType; - QualType ThisType = BaseType; - if (ConstArg) - ArgType.addConst(); - Expr *Args[] = { new (Context) OpaqueValueExpr(Loc, ThisType, VK_LValue) - , new (Context) OpaqueValueExpr(Loc, ArgType, VK_LValue) - }; - - OverloadCandidateSet OCS((Loc)); - OverloadCandidateSet::iterator Best; - - AddFunctionCandidates(R.asUnresolvedSet(), Args, 2, OCS); - - if (OCS.BestViableFunction(*this, Loc, Best, false) != - OR_Success) + CXXMethodDecl *CopyOper = LookupCopyingAssignment(BaseDecl, ArgQuals, false, + 0); + if (!CopyOper || CopyOper->isDeleted()) + return true; + if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible) return true; } @@ -3908,37 +3831,12 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { return true; } - LookupQualifiedName(R, FieldRecord, false); - - // Filter out any result that isn't a copy-assignment operator. - LookupResult::Filter F = R.makeFilter(); - while (F.hasNext()) { - NamedDecl *D = F.next(); - if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(D)) - if (Method->isCopyAssignmentOperator()) - continue; - - F.erase(); - } - F.done(); - - // Build a fake argument expression - QualType ArgType = FieldType; - QualType ThisType = FieldType; - if (ConstArg) - ArgType.addConst(); - Expr *Args[] = { new (Context) OpaqueValueExpr(Loc, ThisType, VK_LValue) - , new (Context) OpaqueValueExpr(Loc, ArgType, VK_LValue) - }; - - OverloadCandidateSet OCS((Loc)); - OverloadCandidateSet::iterator Best; - - AddFunctionCandidates(R.asUnresolvedSet(), Args, 2, OCS); - - if (OCS.BestViableFunction(*this, Loc, Best, false) != - OR_Success) - return true; + CXXMethodDecl *CopyOper = LookupCopyingAssignment(FieldRecord, ArgQuals, + false, 0); + if (!CopyOper || CopyOper->isDeleted()) + return false; + if (CheckDirectMemberAccess(Loc, CopyOper, PDiag()) != AR_accessible) + return false; } } @@ -3948,7 +3846,7 @@ bool Sema::ShouldDeleteCopyAssignmentOperator(CXXMethodDecl *MD) { bool Sema::ShouldDeleteDestructor(CXXDestructorDecl *DD) { CXXRecordDecl *RD = DD->getParent(); assert(!RD->isDependentType() && "do deletion after instantiation"); - if (!LangOpts.CPlusPlus0x) + if (!LangOpts.CPlusPlus0x || RD->isInvalidDecl()) return false; SourceLocation Loc = DD->getLocation(); @@ -4806,6 +4704,13 @@ Decl *Sema::ActOnStartNamespaceDef(Scope *NamespcScope, // Make our StdNamespace cache point at the first real definition of the // "std" namespace. StdNamespace = Namespc; + + // Add this instance of "std" to the set of known namespaces + KnownNamespaces[Namespc] = false; + } else if (!Namespc->isInline()) { + // Since this is an "original" namespace, add it to the known set of + // namespaces if it is not an inline namespace. + KnownNamespaces[Namespc] = false; } PushOnScopeChains(Namespc, DeclRegionScope); @@ -4941,6 +4846,39 @@ static bool IsUsingDirectiveInToplevelContext(DeclContext *CurContext) { } } +static bool TryNamespaceTypoCorrection(Sema &S, LookupResult &R, Scope *Sc, + CXXScopeSpec &SS, + SourceLocation IdentLoc, + IdentifierInfo *Ident) { + R.clear(); + if (TypoCorrection Corrected = S.CorrectTypo(R.getLookupNameInfo(), + R.getLookupKind(), Sc, &SS, NULL, + false, S.CTC_NoKeywords, NULL)) { + if (Corrected.getCorrectionDeclAs<NamespaceDecl>() || + Corrected.getCorrectionDeclAs<NamespaceAliasDecl>()) { + std::string CorrectedStr(Corrected.getAsString(S.getLangOptions())); + std::string CorrectedQuotedStr(Corrected.getQuoted(S.getLangOptions())); + if (DeclContext *DC = S.computeDeclContext(SS, false)) + S.Diag(IdentLoc, diag::err_using_directive_member_suggest) + << Ident << DC << CorrectedQuotedStr << SS.getRange() + << FixItHint::CreateReplacement(IdentLoc, CorrectedStr); + else + S.Diag(IdentLoc, diag::err_using_directive_suggest) + << Ident << CorrectedQuotedStr + << FixItHint::CreateReplacement(IdentLoc, CorrectedStr); + + S.Diag(Corrected.getCorrectionDecl()->getLocation(), + diag::note_namespace_defined_here) << CorrectedQuotedStr; + + Ident = Corrected.getCorrectionAsIdentifierInfo(); + R.addDecl(Corrected.getCorrectionDecl()); + return true; + } + R.setLookupName(Ident); + } + return false; +} + Decl *Sema::ActOnUsingDirective(Scope *S, SourceLocation UsingLoc, SourceLocation NamespcLoc, @@ -4969,6 +4907,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, return 0; if (R.empty()) { + R.clear(); // Allow "using namespace std;" or "using namespace ::std;" even if // "std" hasn't been defined yet, for GCC compatibility. if ((!Qualifier || Qualifier->getKind() == NestedNameSpecifier::Global) && @@ -4978,27 +4917,7 @@ Decl *Sema::ActOnUsingDirective(Scope *S, R.resolveKind(); } // Otherwise, attempt typo correction. - else if (DeclarationName Corrected = CorrectTypo(R, S, &SS, 0, false, - CTC_NoKeywords, 0)) { - if (R.getAsSingle<NamespaceDecl>() || - R.getAsSingle<NamespaceAliasDecl>()) { - if (DeclContext *DC = computeDeclContext(SS, false)) - Diag(IdentLoc, diag::err_using_directive_member_suggest) - << NamespcName << DC << Corrected << SS.getRange() - << FixItHint::CreateReplacement(IdentLoc, Corrected.getAsString()); - else - Diag(IdentLoc, diag::err_using_directive_suggest) - << NamespcName << Corrected - << FixItHint::CreateReplacement(IdentLoc, Corrected.getAsString()); - Diag(R.getFoundDecl()->getLocation(), diag::note_namespace_defined_here) - << Corrected; - - NamespcName = Corrected.getAsIdentifierInfo(); - } else { - R.clear(); - R.setLookupName(NamespcName); - } - } + else TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, NamespcName); } if (!R.empty()) { @@ -5065,6 +4984,7 @@ Decl *Sema::ActOnUsingDeclaration(Scope *S, assert(S->getFlags() & Scope::DeclScope && "Invalid Scope."); switch (Name.getKind()) { + case UnqualifiedId::IK_ImplicitSelfParam: case UnqualifiedId::IK_Identifier: case UnqualifiedId::IK_OperatorFunctionId: case UnqualifiedId::IK_LiteralOperatorId: @@ -5916,30 +5836,7 @@ Decl *Sema::ActOnNamespaceAliasDef(Scope *S, return 0; if (R.empty()) { - if (DeclarationName Corrected = CorrectTypo(R, S, &SS, 0, false, - CTC_NoKeywords, 0)) { - if (R.getAsSingle<NamespaceDecl>() || - R.getAsSingle<NamespaceAliasDecl>()) { - if (DeclContext *DC = computeDeclContext(SS, false)) - Diag(IdentLoc, diag::err_using_directive_member_suggest) - << Ident << DC << Corrected << SS.getRange() - << FixItHint::CreateReplacement(IdentLoc, Corrected.getAsString()); - else - Diag(IdentLoc, diag::err_using_directive_suggest) - << Ident << Corrected - << FixItHint::CreateReplacement(IdentLoc, Corrected.getAsString()); - - Diag(R.getFoundDecl()->getLocation(), diag::note_namespace_defined_here) - << Corrected; - - Ident = Corrected.getAsIdentifierInfo(); - } else { - R.clear(); - R.setLookupName(Ident); - } - } - - if (R.empty()) { + if (!TryNamespaceTypoCorrection(*this, R, S, SS, IdentLoc, Ident)) { Diag(NamespaceLoc, diag::err_expected_namespace_name) << SS.getRange(); return 0; } @@ -5982,6 +5879,8 @@ Sema::ComputeDefaultedDefaultCtorExceptionSpec(CXXRecordDecl *ClassDecl) { // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] ImplicitExceptionSpecification ExceptSpec(Context); + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; // Direct base-class constructors. for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), @@ -6355,7 +6254,9 @@ Sema::ComputeDefaultedDtorExceptionSpec(CXXRecordDecl *ClassDecl) { // An implicitly declared special member function (Clause 12) shall have // an exception-specification. ImplicitExceptionSpecification ExceptSpec(Context); - + if (ClassDecl->isInvalidDecl()) + return ExceptSpec; + // Direct base-class destructors. for (CXXRecordDecl::base_class_iterator B = ClassDecl->bases_begin(), BEnd = ClassDecl->bases_end(); @@ -6687,61 +6588,12 @@ BuildSingleCopyAssign(Sema &S, SourceLocation Loc, QualType T, Loc, Copy.take()); } -/// \brief Determine whether the given class has a copy assignment operator -/// that accepts a const-qualified argument. -static bool hasConstCopyAssignment(Sema &S, const CXXRecordDecl *CClass) { - CXXRecordDecl *Class = const_cast<CXXRecordDecl *>(CClass); - - if (!Class->hasDeclaredCopyAssignment()) - S.DeclareImplicitCopyAssignment(Class); - - QualType ClassType = S.Context.getCanonicalType(S.Context.getTypeDeclType(Class)); - DeclarationName OpName - = S.Context.DeclarationNames.getCXXOperatorName(OO_Equal); - - DeclContext::lookup_const_iterator Op, OpEnd; - for (llvm::tie(Op, OpEnd) = Class->lookup(OpName); Op != OpEnd; ++Op) { - // C++ [class.copy]p9: - // A user-declared copy assignment operator is a non-static non-template - // member function of class X with exactly one parameter of type X, X&, - // const X&, volatile X& or const volatile X&. - const CXXMethodDecl* Method = dyn_cast<CXXMethodDecl>(*Op); - if (!Method) - continue; - - if (Method->isStatic()) - continue; - if (Method->getPrimaryTemplate()) - continue; - const FunctionProtoType *FnType = - Method->getType()->getAs<FunctionProtoType>(); - assert(FnType && "Overloaded operator has no prototype."); - // Don't assert on this; an invalid decl might have been left in the AST. - if (FnType->getNumArgs() != 1 || FnType->isVariadic()) - continue; - bool AcceptsConst = true; - QualType ArgType = FnType->getArgType(0); - if (const LValueReferenceType *Ref = ArgType->getAs<LValueReferenceType>()){ - ArgType = Ref->getPointeeType(); - // Is it a non-const lvalue reference? - if (!ArgType.isConstQualified()) - AcceptsConst = false; - } - if (!S.Context.hasSameUnqualifiedType(ArgType, ClassType)) - continue; - - // We have a single argument of type cv X or cv X&, i.e. we've found the - // copy assignment operator. Return whether it accepts const arguments. - return AcceptsConst; - } - assert(Class->isInvalidDecl() && - "No copy assignment operator declared in valid code."); - return false; -} - std::pair<Sema::ImplicitExceptionSpecification, bool> Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( CXXRecordDecl *ClassDecl) { + if (ClassDecl->isInvalidDecl()) + return std::make_pair(ImplicitExceptionSpecification(Context), false); + // C++ [class.copy]p10: // If the class definition does not explicitly declare a copy // assignment operator, one is declared implicitly. @@ -6759,11 +6611,28 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); HasConstCopyAssignment && Base != BaseEnd; ++Base) { + // We'll handle this below + if (LangOpts.CPlusPlus0x && Base->isVirtual()) + continue; + assert(!Base->getType()->isDependentType() && "Cannot generate implicit members for class with dependent bases."); - const CXXRecordDecl *BaseClassDecl - = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - HasConstCopyAssignment = hasConstCopyAssignment(*this, BaseClassDecl); + CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl(); + LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, false, 0, + &HasConstCopyAssignment); + } + + // In C++0x, the above citation has "or virtual added" + if (LangOpts.CPlusPlus0x) { + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), + BaseEnd = ClassDecl->vbases_end(); + HasConstCopyAssignment && Base != BaseEnd; ++Base) { + assert(!Base->getType()->isDependentType() && + "Cannot generate implicit members for class with dependent bases."); + CXXRecordDecl *BaseClassDecl = Base->getType()->getAsCXXRecordDecl(); + LookupCopyingAssignment(BaseClassDecl, Qualifiers::Const, false, 0, + &HasConstCopyAssignment); + } } // -- for all the nonstatic data members of X that are of a class @@ -6775,10 +6644,9 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( HasConstCopyAssignment && Field != FieldEnd; ++Field) { QualType FieldType = Context.getBaseElementType((*Field)->getType()); - if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) { - const CXXRecordDecl *FieldClassDecl - = cast<CXXRecordDecl>(FieldClassType->getDecl()); - HasConstCopyAssignment = hasConstCopyAssignment(*this, FieldClassDecl); + if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { + LookupCopyingAssignment(FieldClassDecl, Qualifiers::Const, false, 0, + &HasConstCopyAssignment); } } @@ -6790,36 +6658,48 @@ Sema::ComputeDefaultedCopyAssignmentExceptionSpecAndConst( // C++ [except.spec]p14: // An implicitly declared special member function (Clause 12) shall have an // exception-specification. [...] + + // It is unspecified whether or not an implicit copy assignment operator + // attempts to deduplicate calls to assignment operators of virtual bases are + // made. As such, this exception specification is effectively unspecified. + // Based on a similar decision made for constness in C++0x, we're erring on + // the side of assuming such calls to be made regardless of whether they + // actually happen. ImplicitExceptionSpecification ExceptSpec(Context); + unsigned ArgQuals = HasConstCopyAssignment ? Qualifiers::Const : 0; for (CXXRecordDecl::base_class_iterator Base = ClassDecl->bases_begin(), BaseEnd = ClassDecl->bases_end(); Base != BaseEnd; ++Base) { + if (Base->isVirtual()) + continue; + CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - - if (!BaseClassDecl->hasDeclaredCopyAssignment()) - DeclareImplicitCopyAssignment(BaseClassDecl); + if (CXXMethodDecl *CopyAssign = LookupCopyingAssignment(BaseClassDecl, + ArgQuals, false, 0)) + ExceptSpec.CalledDecl(CopyAssign); + } - if (CXXMethodDecl *CopyAssign - = BaseClassDecl->getCopyAssignmentOperator(HasConstCopyAssignment)) + for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), + BaseEnd = ClassDecl->vbases_end(); + Base != BaseEnd; ++Base) { + CXXRecordDecl *BaseClassDecl + = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); + if (CXXMethodDecl *CopyAssign = LookupCopyingAssignment(BaseClassDecl, + ArgQuals, false, 0)) ExceptSpec.CalledDecl(CopyAssign); } + for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), FieldEnd = ClassDecl->field_end(); Field != FieldEnd; ++Field) { QualType FieldType = Context.getBaseElementType((*Field)->getType()); - if (const RecordType *FieldClassType = FieldType->getAs<RecordType>()) { - CXXRecordDecl *FieldClassDecl - = cast<CXXRecordDecl>(FieldClassType->getDecl()); - - if (!FieldClassDecl->hasDeclaredCopyAssignment()) - DeclareImplicitCopyAssignment(FieldClassDecl); - - if (CXXMethodDecl *CopyAssign - = FieldClassDecl->getCopyAssignmentOperator(HasConstCopyAssignment)) - ExceptSpec.CalledDecl(CopyAssign); - } + if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { + if (CXXMethodDecl *CopyAssign = + LookupCopyingAssignment(FieldClassDecl, ArgQuals, false, 0)) + ExceptSpec.CalledDecl(CopyAssign); + } } return std::make_pair(ExceptSpec, HasConstCopyAssignment); @@ -6875,7 +6755,13 @@ CXXMethodDecl *Sema::DeclareImplicitCopyAssignment(CXXRecordDecl *ClassDecl) { PushOnScopeChains(CopyAssignment, S, false); ClassDecl->addDecl(CopyAssignment); - if (ShouldDeleteCopyAssignmentOperator(CopyAssignment)) + // C++0x [class.copy]p18: + // ... If the class definition declares a move constructor or move + // assignment operator, the implicitly declared copy assignment operator is + // defined as deleted; ... + if (ClassDecl->hasUserDeclaredMoveConstructor() || + ClassDecl->hasUserDeclaredMoveAssignment() || + ShouldDeleteCopyAssignmentOperator(CopyAssignment)) CopyAssignment->setDeletedAsWritten(); AddOverriddenMethods(ClassDecl, CopyAssignment); @@ -7014,6 +6900,11 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, Invalid = true; continue; } + + // Suppress assigning zero-width bitfields. + if (const Expr *Width = Field->getBitWidth()) + if (Width->EvaluateAsInt(Context) == 0) + continue; QualType FieldType = Field->getType().getNonReferenceType(); if (FieldType->isIncompleteArrayType()) { @@ -7041,10 +6932,8 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, // explicit assignments, do so. This optimization only applies for arrays // of scalars and arrays of class type with trivial copy-assignment // operators. - if (FieldType->isArrayType() && - (!BaseType->isRecordType() || - cast<CXXRecordDecl>(BaseType->getAs<RecordType>()->getDecl()) - ->hasTrivialCopyAssignment())) { + if (FieldType->isArrayType() && + BaseType.hasTrivialCopyAssignment(Context)) { // Compute the size of the memory buffer to be copied. QualType SizeType = Context.getSizeType(); llvm::APInt Size(Context.getTypeSize(SizeType), @@ -7179,6 +7068,9 @@ void Sema::DefineImplicitCopyAssignment(SourceLocation CurrentLocation, std::pair<Sema::ImplicitExceptionSpecification, bool> Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { + if (ClassDecl->isInvalidDecl()) + return std::make_pair(ImplicitExceptionSpecification(Context), false); + // C++ [class.copy]p5: // The implicitly-declared copy constructor for a class X will // have the form @@ -7202,8 +7094,8 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - LookupCopyConstructor(BaseClassDecl, Qualifiers::Const, - &HasConstCopyConstructor); + LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const, + &HasConstCopyConstructor); } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), @@ -7212,8 +7104,8 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { ++Base) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); - LookupCopyConstructor(BaseClassDecl, Qualifiers::Const, - &HasConstCopyConstructor); + LookupCopyingConstructor(BaseClassDecl, Qualifiers::Const, + &HasConstCopyConstructor); } // -- for all the nonstatic data members of X that are of a @@ -7226,8 +7118,8 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { ++Field) { QualType FieldType = Context.getBaseElementType((*Field)->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { - LookupCopyConstructor(FieldClassDecl, Qualifiers::Const, - &HasConstCopyConstructor); + LookupCopyingConstructor(FieldClassDecl, Qualifiers::Const, + &HasConstCopyConstructor); } } // Otherwise, the implicitly declared copy constructor will have @@ -7251,7 +7143,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); if (CXXConstructorDecl *CopyConstructor = - LookupCopyConstructor(BaseClassDecl, Quals)) + LookupCopyingConstructor(BaseClassDecl, Quals)) ExceptSpec.CalledDecl(CopyConstructor); } for (CXXRecordDecl::base_class_iterator Base = ClassDecl->vbases_begin(), @@ -7261,7 +7153,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { CXXRecordDecl *BaseClassDecl = cast<CXXRecordDecl>(Base->getType()->getAs<RecordType>()->getDecl()); if (CXXConstructorDecl *CopyConstructor = - LookupCopyConstructor(BaseClassDecl, Quals)) + LookupCopyingConstructor(BaseClassDecl, Quals)) ExceptSpec.CalledDecl(CopyConstructor); } for (CXXRecordDecl::field_iterator Field = ClassDecl->field_begin(), @@ -7271,7 +7163,7 @@ Sema::ComputeDefaultedCopyCtorExceptionSpecAndConst(CXXRecordDecl *ClassDecl) { QualType FieldType = Context.getBaseElementType((*Field)->getType()); if (CXXRecordDecl *FieldClassDecl = FieldType->getAsCXXRecordDecl()) { if (CXXConstructorDecl *CopyConstructor = - LookupCopyConstructor(FieldClassDecl, Quals)) + LookupCopyingConstructor(FieldClassDecl, Quals)) ExceptSpec.CalledDecl(CopyConstructor); } } @@ -7334,7 +7226,13 @@ CXXConstructorDecl *Sema::DeclareImplicitCopyConstructor( PushOnScopeChains(CopyConstructor, S, false); ClassDecl->addDecl(CopyConstructor); - if (ShouldDeleteCopyConstructor(CopyConstructor)) + // C++0x [class.copy]p7: + // ... If the class definition declares a move constructor or move + // assignment operator, the implicitly declared constructor is defined as + // deleted; ... + if (ClassDecl->hasUserDeclaredMoveConstructor() || + ClassDecl->hasUserDeclaredMoveAssignment() || + ShouldDeleteCopyConstructor(CopyConstructor)) CopyConstructor->setDeletedAsWritten(); return CopyConstructor; @@ -7523,6 +7421,10 @@ void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl, VDecl->setTypeSourceInfo(DeducedType); VDecl->setType(DeducedType->getType()); + // In ARC, infer lifetime. + if (getLangOptions().ObjCAutoRefCount && inferObjCARCLifetime(VDecl)) + VDecl->setInvalidDecl(); + // If this is a redeclaration, check that the type we just deduced matches // the previously declared type. if (VarDecl *Old = VDecl->getPreviousDeclaration()) @@ -7605,9 +7507,9 @@ void Sema::AddCXXDirectInitializerToDecl(Decl *RealDecl, // Store the initialization expressions as a ParenListExpr. unsigned NumExprs = Exprs.size(); - VDecl->setInit(new (Context) ParenListExpr(Context, LParenLoc, - (Expr **)Exprs.release(), - NumExprs, RParenLoc)); + VDecl->setInit(new (Context) ParenListExpr( + Context, LParenLoc, (Expr **)Exprs.release(), NumExprs, RParenLoc, + VDecl->getType().getNonReferenceType())); return; } @@ -8143,10 +8045,8 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, Diag(Loc, diag::err_objc_object_catch); Invalid = true; } else if (T->isObjCObjectPointerType()) { - if (!getLangOptions().ObjCNonFragileABI) { - Diag(Loc, diag::err_objc_pointer_cxx_catch_fragile); - Invalid = true; - } + if (!getLangOptions().ObjCNonFragileABI) + Diag(Loc, diag::warn_objc_pointer_cxx_catch_fragile); } } @@ -8154,7 +8054,7 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, ExDeclType, TInfo, SC_None, SC_None); ExDecl->setExceptionVariable(true); - if (!Invalid) { + if (!Invalid && !ExDeclType->isDependentType()) { if (const RecordType *recordType = ExDeclType->getAs<RecordType>()) { // C++ [except.handle]p16: // The object declared in an exception-declaration or, if the @@ -9020,25 +8920,16 @@ DeclResult Sema::ActOnCXXConditionDeclaration(Scope *S, Declarator &D) { // new class or enumeration. assert(D.getDeclSpec().getStorageClassSpec() != DeclSpec::SCS_typedef && "Parser allowed 'typedef' as storage class of condition decl."); - - TagDecl *OwnedTag = 0; - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S, &OwnedTag); - QualType Ty = TInfo->getType(); - - if (Ty->isFunctionType()) { // The declarator shall not specify a function... - // We exit without creating a CXXConditionDeclExpr because a FunctionDecl - // would be created and CXXConditionDeclExpr wants a VarDecl. - Diag(D.getIdentifierLoc(), diag::err_invalid_use_of_function_type) - << D.getSourceRange(); - return DeclResult(); - } else if (OwnedTag && OwnedTag->isDefinition()) { - // The type-specifier-seq shall not declare a new class or enumeration. - Diag(OwnedTag->getLocation(), diag::err_type_defined_in_condition); - } - + Decl *Dcl = ActOnDeclarator(S, D); if (!Dcl) - return DeclResult(); + return true; + + if (isa<FunctionDecl>(Dcl)) { // The declarator shall not specify a function. + Diag(Dcl->getLocation(), diag::err_invalid_use_of_function_type) + << D.getSourceRange(); + return true; + } return Dcl; } diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index de9097e98b4f..aa8152b03b0a 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -16,14 +16,96 @@ #include "clang/Sema/ExternalSemaSource.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/AST/ASTConsumer.h" #include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" #include "clang/Sema/DeclSpec.h" #include "llvm/ADT/DenseSet.h" using namespace clang; +/// Check whether the given method, which must be in the 'init' +/// family, is a valid member of that family. +/// +/// \param receiverTypeIfCall - if null, check this as if declaring it; +/// if non-null, check this as if making a call to it with the given +/// receiver type +/// +/// \return true to indicate that there was an error and appropriate +/// actions were taken +bool Sema::checkInitMethod(ObjCMethodDecl *method, + QualType receiverTypeIfCall) { + if (method->isInvalidDecl()) return true; + + // This castAs is safe: methods that don't return an object + // pointer won't be inferred as inits and will reject an explicit + // objc_method_family(init). + + // We ignore protocols here. Should we? What about Class? + + const ObjCObjectType *result = method->getResultType() + ->castAs<ObjCObjectPointerType>()->getObjectType(); + + if (result->isObjCId()) { + return false; + } else if (result->isObjCClass()) { + // fall through: always an error + } else { + ObjCInterfaceDecl *resultClass = result->getInterface(); + assert(resultClass && "unexpected object type!"); + + // It's okay for the result type to still be a forward declaration + // if we're checking an interface declaration. + if (resultClass->isForwardDecl()) { + if (receiverTypeIfCall.isNull() && + !isa<ObjCImplementationDecl>(method->getDeclContext())) + return false; + + // Otherwise, we try to compare class types. + } else { + // If this method was declared in a protocol, we can't check + // anything unless we have a receiver type that's an interface. + const ObjCInterfaceDecl *receiverClass = 0; + if (isa<ObjCProtocolDecl>(method->getDeclContext())) { + if (receiverTypeIfCall.isNull()) + return false; + + receiverClass = receiverTypeIfCall->castAs<ObjCObjectPointerType>() + ->getInterfaceDecl(); + + // This can be null for calls to e.g. id<Foo>. + if (!receiverClass) return false; + } else { + receiverClass = method->getClassInterface(); + assert(receiverClass && "method not associated with a class!"); + } + + // If either class is a subclass of the other, it's fine. + if (receiverClass->isSuperClassOf(resultClass) || + resultClass->isSuperClassOf(receiverClass)) + return false; + } + } + + SourceLocation loc = method->getLocation(); + + // If we're in a system header, and this is not a call, just make + // the method unusable. + if (receiverTypeIfCall.isNull() && getSourceManager().isInSystemHeader(loc)) { + method->addAttr(new (Context) UnavailableAttr(loc, Context, + "init method returns a type unrelated to its receiver type")); + return true; + } + + // Otherwise, it's an error. + Diag(loc, diag::err_arc_init_method_unrelated_result_type); + method->setInvalidDecl(); + return true; +} + bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, const ObjCMethodDecl *Overridden, bool IsImplementation) { @@ -36,7 +118,7 @@ bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, QualType ResultType = NewMethod->getResultType(); SourceRange ResultTypeRange; if (const TypeSourceInfo *ResultTypeInfo - = NewMethod->getResultTypeSourceInfo()) + = NewMethod->getResultTypeSourceInfo()) ResultTypeRange = ResultTypeInfo->getTypeLoc().getSourceRange(); // Figure out which class this method is part of, if any. @@ -73,7 +155,15 @@ bool Sema::CheckObjCMethodOverride(ObjCMethodDecl *NewMethod, return false; } - +/// \brief Check for consistency between a given method declaration and the +/// methods it overrides within the class hierarchy. +/// +/// This method walks the inheritance hierarchy starting at the given +/// declaration context (\p DC), invoking Sema::CheckObjCMethodOverride() with +/// the given new method (\p NewMethod) and any method it directly overrides +/// in the hierarchy. Sema::CheckObjCMethodOverride() is responsible for +/// checking consistency, e.g., among return types for methods that return a +/// related result type. static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, DeclContext *DC, bool SkipCurrent = true) { @@ -99,10 +189,10 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (CheckObjCMethodOverrides(S, NewMethod, Category, false)) return true; } - + // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Class->protocol_begin(), - IEnd = Class->protocol_end(); + IEnd = Class->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -115,7 +205,7 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) { // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Category->protocol_begin(), - IEnd = Category->protocol_end(); + IEnd = Category->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -126,7 +216,7 @@ static bool CheckObjCMethodOverrides(Sema &S, ObjCMethodDecl *NewMethod, if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) { // Look through protocols. for (ObjCList<ObjCProtocolDecl>::iterator I = Protocol->protocol_begin(), - IEnd = Protocol->protocol_end(); + IEnd = Protocol->protocol_end(); I != IEnd; ++I) if (CheckObjCMethodOverrides(S, NewMethod, *I, false)) return true; @@ -141,13 +231,13 @@ bool Sema::CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, DeclContext *DC) { if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Class); - + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Category); - + if (ObjCProtocolDecl *Protocol = dyn_cast<ObjCProtocolDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Protocol); - + if (ObjCImplementationDecl *Impl = dyn_cast<ObjCImplementationDecl>(DC)) return ::CheckObjCMethodOverrides(*this, NewMethod, Impl->getClassInterface()); @@ -159,6 +249,55 @@ bool Sema::CheckObjCMethodOverrides(ObjCMethodDecl *NewMethod, return ::CheckObjCMethodOverrides(*this, NewMethod, CurContext); } +/// \brief Check a method declaration for compatibility with the Objective-C +/// ARC conventions. +static bool CheckARCMethodDecl(Sema &S, ObjCMethodDecl *method) { + ObjCMethodFamily family = method->getMethodFamily(); + switch (family) { + case OMF_None: + case OMF_dealloc: + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + case OMF_self: + return false; + + case OMF_init: + // If the method doesn't obey the init rules, don't bother annotating it. + if (S.checkInitMethod(method, QualType())) + return true; + + method->addAttr(new (S.Context) NSConsumesSelfAttr(SourceLocation(), + S.Context)); + + // Don't add a second copy of this attribute, but otherwise don't + // let it be suppressed. + if (method->hasAttr<NSReturnsRetainedAttr>()) + return false; + break; + + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + if (method->hasAttr<NSReturnsRetainedAttr>() || + method->hasAttr<NSReturnsNotRetainedAttr>() || + method->hasAttr<NSReturnsAutoreleasedAttr>()) + return false; + break; + + case OMF_performSelector: + // we don't annotate performSelector's + return true; + + } + + method->addAttr(new (S.Context) NSReturnsRetainedAttr(SourceLocation(), + S.Context)); + return false; +} + static void DiagnoseObjCImplementedDeprecations(Sema &S, NamedDecl *ND, SourceLocation ImplLoc, @@ -212,6 +351,31 @@ void Sema::ActOnStartOfObjCMethodDef(Scope *FnBodyScope, Decl *D) { if ((*PI)->getIdentifier()) PushOnScopeChains(*PI, FnBodyScope); } + + // In ARC, disallow definition of retain/release/autorelease/retainCount + if (getLangOptions().ObjCAutoRefCount) { + switch (MDecl->getMethodFamily()) { + case OMF_retain: + case OMF_retainCount: + case OMF_release: + case OMF_autorelease: + Diag(MDecl->getLocation(), diag::err_arc_illegal_method_def) + << MDecl->getSelector(); + break; + + case OMF_None: + case OMF_dealloc: + case OMF_alloc: + case OMF_init: + case OMF_mutableCopy: + case OMF_copy: + case OMF_new: + case OMF_self: + case OMF_performSelector: + break; + } + } + // Warn on implementating deprecated methods under // -Wdeprecated-implementations flag. if (ObjCInterfaceDecl *IC = MDecl->getClassInterface()) @@ -284,9 +448,10 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, if (!PrevDecl) { // Try to correct for a typo in the superclass name. - LookupResult R(*this, SuperName, SuperLoc, LookupOrdinaryName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (PrevDecl = R.getAsSingle<ObjCInterfaceDecl>())) { + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(SuperName, SuperLoc), LookupOrdinaryName, TUScope, + NULL, NULL, false, CTC_NoKeywords); + if ((PrevDecl = Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>())) { Diag(SuperLoc, diag::err_undef_superclass_suggest) << SuperName << ClassName << PrevDecl->getDeclName(); Diag(PrevDecl->getLocation(), diag::note_previous_decl) @@ -333,10 +498,13 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, if (!SuperClassDecl) Diag(SuperLoc, diag::err_undef_superclass) << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); - else if (SuperClassDecl->isForwardDecl()) - Diag(SuperLoc, diag::err_undef_superclass) + else if (SuperClassDecl->isForwardDecl()) { + Diag(SuperLoc, diag::err_forward_superclass) << SuperClassDecl->getDeclName() << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); + Diag(SuperClassDecl->getLocation(), diag::note_forward_class); + SuperClassDecl = 0; + } } IDecl->setSuperClass(SuperClassDecl); IDecl->setSuperClassLoc(SuperLoc); @@ -494,12 +662,12 @@ Sema::FindProtocolDeclaration(bool WarnOnDeclarations, ObjCProtocolDecl *PDecl = LookupProtocol(ProtocolId[i].first, ProtocolId[i].second); if (!PDecl) { - LookupResult R(*this, ProtocolId[i].first, ProtocolId[i].second, - LookupObjCProtocolName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (PDecl = R.getAsSingle<ObjCProtocolDecl>())) { + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(ProtocolId[i].first, ProtocolId[i].second), + LookupObjCProtocolName, TUScope, NULL, NULL, false, CTC_NoKeywords); + if ((PDecl = Corrected.getCorrectionDeclAs<ObjCProtocolDecl>())) { Diag(ProtocolId[i].second, diag::err_undeclared_protocol_suggest) - << ProtocolId[i].first << R.getLookupName(); + << ProtocolId[i].first << Corrected.getCorrection(); Diag(PDecl->getLocation(), diag::note_previous_decl) << PDecl->getDeclName(); } @@ -736,20 +904,20 @@ Decl *Sema::ActOnStartClassImplementation( } else { // We did not find anything with the name ClassName; try to correct for // typos in the class name. - LookupResult R(*this, ClassName, ClassLoc, LookupOrdinaryName); - if (CorrectTypo(R, TUScope, 0, 0, false, CTC_NoKeywords) && - (IDecl = R.getAsSingle<ObjCInterfaceDecl>())) { + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(ClassName, ClassLoc), LookupOrdinaryName, TUScope, + NULL, NULL, false, CTC_NoKeywords); + if ((IDecl = Corrected.getCorrectionDeclAs<ObjCInterfaceDecl>())) { // Suggest the (potentially) correct interface name. However, put the // fix-it hint itself in a separate note, since changing the name in // the warning would make the fix-it change semantics.However, don't // provide a code-modification hint or use the typo name for recovery, // because this is just a warning. The program may actually be correct. + DeclarationName CorrectedName = Corrected.getCorrection(); Diag(ClassLoc, diag::warn_undef_interface_suggest) - << ClassName << R.getLookupName(); - Diag(IDecl->getLocation(), diag::note_previous_decl) - << R.getLookupName() - << FixItHint::CreateReplacement(ClassLoc, - R.getLookupName().getAsString()); + << ClassName << CorrectedName; + Diag(IDecl->getLocation(), diag::note_previous_decl) << CorrectedName + << FixItHint::CreateReplacement(ClassLoc, CorrectedName.getAsString()); IDecl = 0; } else { Diag(ClassLoc, diag::warn_undef_interface) << ClassName; @@ -915,6 +1083,9 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl, void Sema::WarnUndefinedMethod(SourceLocation ImpLoc, ObjCMethodDecl *method, bool &IncompleteImpl, unsigned DiagID) { + // No point warning no definition of method which is 'unavailable'. + if (method->hasAttr<UnavailableAttr>()) + return; if (!IncompleteImpl) { Diag(ImpLoc, diag::warn_incomplete_impl); IncompleteImpl = true; @@ -1091,11 +1262,84 @@ static void CheckMethodOverrideParam(Sema &S, S.Diag(IfaceVar->getLocation(), diag::note_previous_definition) << getTypeRange(IfaceVar->getTypeSourceInfo()); } - + +/// In ARC, check whether the conventional meanings of the two methods +/// match. If they don't, it's a hard error. +static bool checkMethodFamilyMismatch(Sema &S, ObjCMethodDecl *impl, + ObjCMethodDecl *decl) { + ObjCMethodFamily implFamily = impl->getMethodFamily(); + ObjCMethodFamily declFamily = decl->getMethodFamily(); + if (implFamily == declFamily) return false; + + // Since conventions are sorted by selector, the only possibility is + // that the types differ enough to cause one selector or the other + // to fall out of the family. + assert(implFamily == OMF_None || declFamily == OMF_None); + + // No further diagnostics required on invalid declarations. + if (impl->isInvalidDecl() || decl->isInvalidDecl()) return true; + + const ObjCMethodDecl *unmatched = impl; + ObjCMethodFamily family = declFamily; + unsigned errorID = diag::err_arc_lost_method_convention; + unsigned noteID = diag::note_arc_lost_method_convention; + if (declFamily == OMF_None) { + unmatched = decl; + family = implFamily; + errorID = diag::err_arc_gained_method_convention; + noteID = diag::note_arc_gained_method_convention; + } + + // Indexes into a %select clause in the diagnostic. + enum FamilySelector { + F_alloc, F_copy, F_mutableCopy = F_copy, F_init, F_new + }; + FamilySelector familySelector = FamilySelector(); + + switch (family) { + case OMF_None: llvm_unreachable("logic error, no method convention"); + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_dealloc: + case OMF_retainCount: + case OMF_self: + case OMF_performSelector: + // Mismatches for these methods don't change ownership + // conventions, so we don't care. + return false; + + case OMF_init: familySelector = F_init; break; + case OMF_alloc: familySelector = F_alloc; break; + case OMF_copy: familySelector = F_copy; break; + case OMF_mutableCopy: familySelector = F_mutableCopy; break; + case OMF_new: familySelector = F_new; break; + } + + enum ReasonSelector { R_NonObjectReturn, R_UnrelatedReturn }; + ReasonSelector reasonSelector; + + // The only reason these methods don't fall within their families is + // due to unusual result types. + if (unmatched->getResultType()->isObjCObjectPointerType()) { + reasonSelector = R_UnrelatedReturn; + } else { + reasonSelector = R_NonObjectReturn; + } + + S.Diag(impl->getLocation(), errorID) << familySelector << reasonSelector; + S.Diag(decl->getLocation(), noteID) << familySelector << reasonSelector; + + return true; +} void Sema::WarnConflictingTypedMethods(ObjCMethodDecl *ImpMethodDecl, ObjCMethodDecl *MethodDecl, bool IsProtocolMethodDecl) { + if (getLangOptions().ObjCAutoRefCount && + checkMethodFamilyMismatch(*this, ImpMethodDecl, MethodDecl)) + return; + CheckMethodOverrideReturn(*this, ImpMethodDecl, MethodDecl, IsProtocolMethodDecl); @@ -1204,7 +1448,7 @@ void Sema::CheckProtocolMethodDefs(SourceLocation ImpLoc, CheckProtocolMethodDefs(ImpLoc, *PI, IncompleteImpl, InsMap, ClsMap, IDecl); } -/// MatchAllMethodDeclarations - Check methods declaraed in interface or +/// MatchAllMethodDeclarations - Check methods declared in interface /// or protocol against those declared in their implementations. /// void Sema::MatchAllMethodDeclarations(const llvm::DenseSet<Selector> &InsMap, @@ -1422,48 +1666,117 @@ Sema::ActOnForwardClassDeclaration(SourceLocation AtClassLoc, return CDecl; } +static bool tryMatchRecordTypes(ASTContext &Context, + Sema::MethodMatchStrategy strategy, + const Type *left, const Type *right); + +static bool matchTypes(ASTContext &Context, Sema::MethodMatchStrategy strategy, + QualType leftQT, QualType rightQT) { + const Type *left = + Context.getCanonicalType(leftQT).getUnqualifiedType().getTypePtr(); + const Type *right = + Context.getCanonicalType(rightQT).getUnqualifiedType().getTypePtr(); + + if (left == right) return true; + + // If we're doing a strict match, the types have to match exactly. + if (strategy == Sema::MMS_strict) return false; + + if (left->isIncompleteType() || right->isIncompleteType()) return false; + + // Otherwise, use this absurdly complicated algorithm to try to + // validate the basic, low-level compatibility of the two types. + + // As a minimum, require the sizes and alignments to match. + if (Context.getTypeInfo(left) != Context.getTypeInfo(right)) + return false; + + // Consider all the kinds of non-dependent canonical types: + // - functions and arrays aren't possible as return and parameter types + + // - vector types of equal size can be arbitrarily mixed + if (isa<VectorType>(left)) return isa<VectorType>(right); + if (isa<VectorType>(right)) return false; + + // - references should only match references of identical type + // - structs, unions, and Objective-C objects must match more-or-less + // exactly + // - everything else should be a scalar + if (!left->isScalarType() || !right->isScalarType()) + return tryMatchRecordTypes(Context, strategy, left, right); + + // Make scalars agree in kind, except count bools as chars. + Type::ScalarTypeKind leftSK = left->getScalarTypeKind(); + Type::ScalarTypeKind rightSK = right->getScalarTypeKind(); + if (leftSK == Type::STK_Bool) leftSK = Type::STK_Integral; + if (rightSK == Type::STK_Bool) rightSK = Type::STK_Integral; + + // Note that data member pointers and function member pointers don't + // intermix because of the size differences. + + return (leftSK == rightSK); +} + +static bool tryMatchRecordTypes(ASTContext &Context, + Sema::MethodMatchStrategy strategy, + const Type *lt, const Type *rt) { + assert(lt && rt && lt != rt); + + if (!isa<RecordType>(lt) || !isa<RecordType>(rt)) return false; + RecordDecl *left = cast<RecordType>(lt)->getDecl(); + RecordDecl *right = cast<RecordType>(rt)->getDecl(); + + // Require union-hood to match. + if (left->isUnion() != right->isUnion()) return false; + + // Require an exact match if either is non-POD. + if ((isa<CXXRecordDecl>(left) && !cast<CXXRecordDecl>(left)->isPOD()) || + (isa<CXXRecordDecl>(right) && !cast<CXXRecordDecl>(right)->isPOD())) + return false; + + // Require size and alignment to match. + if (Context.getTypeInfo(lt) != Context.getTypeInfo(rt)) return false; + + // Require fields to match. + RecordDecl::field_iterator li = left->field_begin(), le = left->field_end(); + RecordDecl::field_iterator ri = right->field_begin(), re = right->field_end(); + for (; li != le && ri != re; ++li, ++ri) { + if (!matchTypes(Context, strategy, li->getType(), ri->getType())) + return false; + } + return (li == le && ri == re); +} /// MatchTwoMethodDeclarations - Checks that two methods have matching type and /// returns true, or false, accordingly. /// TODO: Handle protocol list; such as id<p1,p2> in type comparisons -bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *Method, - const ObjCMethodDecl *PrevMethod, - bool matchBasedOnSizeAndAlignment, - bool matchBasedOnStrictEqulity) { - QualType T1 = Context.getCanonicalType(Method->getResultType()); - QualType T2 = Context.getCanonicalType(PrevMethod->getResultType()); - - if (T1 != T2) { - // The result types are different. - if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity) - return false; - // Incomplete types don't have a size and alignment. - if (T1->isIncompleteType() || T2->isIncompleteType()) - return false; - // Check is based on size and alignment. - if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2)) - return false; - } +bool Sema::MatchTwoMethodDeclarations(const ObjCMethodDecl *left, + const ObjCMethodDecl *right, + MethodMatchStrategy strategy) { + if (!matchTypes(Context, strategy, + left->getResultType(), right->getResultType())) + return false; - ObjCMethodDecl::param_iterator ParamI = Method->param_begin(), - E = Method->param_end(); - ObjCMethodDecl::param_iterator PrevI = PrevMethod->param_begin(); + if (getLangOptions().ObjCAutoRefCount && + (left->hasAttr<NSReturnsRetainedAttr>() + != right->hasAttr<NSReturnsRetainedAttr>() || + left->hasAttr<NSConsumesSelfAttr>() + != right->hasAttr<NSConsumesSelfAttr>())) + return false; - for (; ParamI != E; ++ParamI, ++PrevI) { - assert(PrevI != PrevMethod->param_end() && "Param mismatch"); - T1 = Context.getCanonicalType((*ParamI)->getType()); - T2 = Context.getCanonicalType((*PrevI)->getType()); - if (T1 != T2) { - // The result types are different. - if (!matchBasedOnSizeAndAlignment || matchBasedOnStrictEqulity) - return false; - // Incomplete types don't have a size and alignment. - if (T1->isIncompleteType() || T2->isIncompleteType()) - return false; - // Check is based on size and alignment. - if (Context.getTypeInfo(T1) != Context.getTypeInfo(T2)) - return false; - } + ObjCMethodDecl::param_iterator + li = left->param_begin(), le = left->param_end(), ri = right->param_begin(); + + for (; li != le; ++li, ++ri) { + assert(ri != right->param_end() && "Param mismatch"); + ParmVarDecl *lparm = *li, *rparm = *ri; + + if (!matchTypes(Context, strategy, lparm->getType(), rparm->getType())) + return false; + + if (getLangOptions().ObjCAutoRefCount && + lparm->hasAttr<NSConsumedAttr>() != rparm->hasAttr<NSConsumedAttr>()) + return false; } return true; } @@ -1505,8 +1818,10 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, // We've seen a method with this name, see if we have already seen this type // signature. - for (ObjCMethodList *List = &Entry; List; List = List->Next) - if (MatchTwoMethodDeclarations(Method, List->Method)) { + for (ObjCMethodList *List = &Entry; List; List = List->Next) { + bool match = MatchTwoMethodDeclarations(Method, List->Method); + + if (match) { ObjCMethodDecl *PrevObjCMethod = List->Method; PrevObjCMethod->setDefined(impl); // If a method is deprecated, push it in the global pool. @@ -1523,6 +1838,7 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, } return; } + } // We have a new signature for an existing method - add it. // This is extremely rare. Only 1% of Cocoa selectors are "overloaded". @@ -1530,6 +1846,25 @@ void Sema::AddMethodToGlobalPool(ObjCMethodDecl *Method, bool impl, Entry.Next = new (Mem) ObjCMethodList(Method, Entry.Next); } +/// Determines if this is an "acceptable" loose mismatch in the global +/// method pool. This exists mostly as a hack to get around certain +/// global mismatches which we can't afford to make warnings / errors. +/// Really, what we want is a way to take a method out of the global +/// method pool. +static bool isAcceptableMethodMismatch(ObjCMethodDecl *chosen, + ObjCMethodDecl *other) { + if (!chosen->isInstanceMethod()) + return false; + + Selector sel = chosen->getSelector(); + if (!sel.isUnarySelector() || sel.getNameForSlot(0) != "length") + return false; + + // Don't complain about mismatches for -length if the method we + // chose has an integral result type. + return (chosen->getResultType()->isIntegerType()); +} + ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, bool receiverIdOrClass, bool warn, bool instance) { @@ -1543,32 +1878,52 @@ ObjCMethodDecl *Sema::LookupMethodInGlobalPool(Selector Sel, SourceRange R, ObjCMethodList &MethList = instance ? Pos->second.first : Pos->second.second; - bool strictSelectorMatch = receiverIdOrClass && warn && - (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl, - R.getBegin()) != - Diagnostic::Ignored); if (warn && MethList.Method && MethList.Next) { - bool issueWarning = false; + bool issueDiagnostic = false, issueError = false; + + // We support a warning which complains about *any* difference in + // method signature. + bool strictSelectorMatch = + (receiverIdOrClass && warn && + (Diags.getDiagnosticLevel(diag::warn_strict_multiple_method_decl, + R.getBegin()) != + Diagnostic::Ignored)); if (strictSelectorMatch) for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) { - // This checks if the methods differ in type mismatch. - if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, false, true)) - issueWarning = true; + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, + MMS_strict)) { + issueDiagnostic = true; + break; + } } - if (!issueWarning) + // If we didn't see any strict differences, we won't see any loose + // differences. In ARC, however, we also need to check for loose + // mismatches, because most of them are errors. + if (!strictSelectorMatch || + (issueDiagnostic && getLangOptions().ObjCAutoRefCount)) for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) { - // This checks if the methods differ by size & alignment. - if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, true)) - issueWarning = true; + // This checks if the methods differ in type mismatch. + if (!MatchTwoMethodDeclarations(MethList.Method, Next->Method, + MMS_loose) && + !isAcceptableMethodMismatch(MethList.Method, Next->Method)) { + issueDiagnostic = true; + if (getLangOptions().ObjCAutoRefCount) + issueError = true; + break; + } } - if (issueWarning) { - if (strictSelectorMatch) + if (issueDiagnostic) { + if (issueError) + Diag(R.getBegin(), diag::err_arc_multiple_method_decl) << Sel << R; + else if (strictSelectorMatch) Diag(R.getBegin(), diag::warn_strict_multiple_method_decl) << Sel << R; else Diag(R.getBegin(), diag::warn_multiple_method_decl) << Sel << R; - Diag(MethList.Method->getLocStart(), diag::note_using) + + Diag(MethList.Method->getLocStart(), + issueError ? diag::note_possibility : diag::note_using) << MethList.Method->getSourceRange(); for (ObjCMethodList *Next = MethList.Next; Next; Next = Next->Next) Diag(Next->Method->getLocStart(), diag::note_also_found) @@ -1796,6 +2151,7 @@ void Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, DefaultSynthesizeProperties(S, IC, IDecl); ImplMethodsVsClassMethods(S, IC, IDecl); AtomicPropertySetterGetterRules(IC, IDecl); + DiagnoseOwningPropertyGetterSynthesis(IC); if (LangOpts.ObjCNonFragileABI2) while (IDecl->getSuperClass()) { @@ -1968,7 +2324,7 @@ Decl *Sema::ActOnMethodDeclaration( } else { ArgType = GetTypeFromParser(ArgInfo[i].Type, &DI); // Perform the default array/function conversions (C99 6.7.5.3p[7,8]). - ArgType = adjustParameterType(ArgType); + ArgType = Context.getAdjustedParameterType(ArgType); } LookupResult R(*this, ArgInfo[i].Name, ArgInfo[i].NameLoc, @@ -2015,7 +2371,7 @@ Decl *Sema::ActOnMethodDeclaration( ArgType = Context.getObjCIdType(); else // Perform the default array/function conversions (C99 6.7.5.3p[7,8]). - ArgType = adjustParameterType(ArgType); + ArgType = Context.getAdjustedParameterType(ArgType); if (ArgType->isObjCObjectType()) { Diag(Param->getLocation(), diag::err_object_cannot_be_passed_returned_by_value) @@ -2104,7 +2460,11 @@ Decl *Sema::ActOnMethodDeclaration( mergeObjCMethodDecls(ObjCMethod, InterfaceMD); } - if (!ObjCMethod->hasRelatedResultType() && + bool ARCError = false; + if (getLangOptions().ObjCAutoRefCount) + ARCError = CheckARCMethodDecl(*this, ObjCMethod); + + if (!ObjCMethod->hasRelatedResultType() && !ARCError && getLangOptions().ObjCInferRelatedResultType) { bool InferRelatedResultType = false; switch (ObjCMethod->getMethodFamily()) { @@ -2114,6 +2474,7 @@ Decl *Sema::ActOnMethodDeclaration( case OMF_mutableCopy: case OMF_release: case OMF_retainCount: + case OMF_performSelector: break; case OMF_alloc: @@ -2255,15 +2616,8 @@ Decl *Sema::ActOnObjCExceptionDecl(Scope *S, Declarator &D) { if (getLangOptions().CPlusPlus) CheckExtraCXXDefaultArguments(D); - TagDecl *OwnedDecl = 0; - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S, &OwnedDecl); + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType ExceptionType = TInfo->getType(); - - if (getLangOptions().CPlusPlus && OwnedDecl && OwnedDecl->isDefinition()) { - // Objective-C++: Types shall not be defined in exception types. - Diag(OwnedDecl->getLocation(), diag::err_type_defined_in_param_type) - << Context.getTypeDeclType(OwnedDecl); - } VarDecl *New = BuildObjCExceptionDecl(TInfo, ExceptionType, D.getSourceRange().getBegin(), diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 0549e9499560..5efc36559d97 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -87,7 +87,7 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { if (FD->isDeleted()) { Diag(Loc, diag::err_deleted_function_use); - Diag(D->getLocation(), diag::note_unavailable_here) << true; + Diag(D->getLocation(), diag::note_unavailable_here) << 1 << true; return true; } } @@ -104,17 +104,20 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, SourceLocation Loc, break; case AR_Unavailable: - if (Message.empty()) { - if (!UnknownObjCClass) - Diag(Loc, diag::err_unavailable) << D->getDeclName(); - else - Diag(Loc, diag::warn_unavailable_fwdclass_message) - << D->getDeclName(); + if (cast<Decl>(CurContext)->getAvailability() != AR_Unavailable) { + if (Message.empty()) { + if (!UnknownObjCClass) + Diag(Loc, diag::err_unavailable) << D->getDeclName(); + else + Diag(Loc, diag::warn_unavailable_fwdclass_message) + << D->getDeclName(); + } + else + Diag(Loc, diag::err_unavailable_message) + << D->getDeclName() << Message; + Diag(D->getLocation(), diag::note_unavailable_here) + << isa<FunctionDecl>(D) << false; } - else - Diag(Loc, diag::err_unavailable_message) - << D->getDeclName() << Message; - Diag(D->getLocation(), diag::note_unavailable_here) << 0; break; } @@ -437,8 +440,12 @@ ExprResult Sema::DefaultArgumentPromotion(Expr *E) { /// will warn if the resulting type is not a POD type, and rejects ObjC /// interfaces passed by value. ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, - FunctionDecl *FDecl) { - ExprResult ExprRes = DefaultArgumentPromotion(E); + FunctionDecl *FDecl) { + ExprResult ExprRes = CheckPlaceholderExpr(E); + if (ExprRes.isInvalid()) + return ExprError(); + + ExprRes = DefaultArgumentPromotion(E); if (ExprRes.isInvalid()) return ExprError(); E = ExprRes.take(); @@ -456,7 +463,7 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, << E->getType() << CT)) return ExprError(); - if (!E->getType()->isPODType()) { + if (!E->getType().isPODType(Context)) { // C++0x [expr.call]p7: // Passing a potentially-evaluated argument of class type (Clause 9) // having a non-trivial copy constructor, a non-trivial move constructor, @@ -471,6 +478,11 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT, TrivialEnough = true; } } + + if (!TrivialEnough && + getLangOptions().ObjCAutoRefCount && + E->getType()->isObjCLifetimeType()) + TrivialEnough = true; if (TrivialEnough) { // Nothing to diagnose. This is okay. @@ -1004,7 +1016,6 @@ Sema::ActOnStringLiteral(const Token *StringToks, unsigned NumStringToks) { // Pass &StringTokLocs[0], StringTokLocs.size() to factory! return Owned(StringLiteral::Create(Context, Literal.GetString(), - Literal.GetStringLength(), Literal.AnyWide, Literal.Pascal, StrTy, &StringTokLocs[0], StringTokLocs.size())); @@ -1271,125 +1282,6 @@ Sema::BuildDeclRefExpr(ValueDecl *D, QualType Ty, ExprValueKind VK, return Owned(E); } -static ExprResult -BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow, - const CXXScopeSpec &SS, FieldDecl *Field, - DeclAccessPair FoundDecl, - const DeclarationNameInfo &MemberNameInfo); - -ExprResult -Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, - SourceLocation loc, - IndirectFieldDecl *indirectField, - Expr *baseObjectExpr, - SourceLocation opLoc) { - // First, build the expression that refers to the base object. - - bool baseObjectIsPointer = false; - Qualifiers baseQuals; - - // Case 1: the base of the indirect field is not a field. - VarDecl *baseVariable = indirectField->getVarDecl(); - CXXScopeSpec EmptySS; - if (baseVariable) { - assert(baseVariable->getType()->isRecordType()); - - // In principle we could have a member access expression that - // accesses an anonymous struct/union that's a static member of - // the base object's class. However, under the current standard, - // static data members cannot be anonymous structs or unions. - // Supporting this is as easy as building a MemberExpr here. - assert(!baseObjectExpr && "anonymous struct/union is static data member?"); - - DeclarationNameInfo baseNameInfo(DeclarationName(), loc); - - ExprResult result = - BuildDeclarationNameExpr(EmptySS, baseNameInfo, baseVariable); - if (result.isInvalid()) return ExprError(); - - baseObjectExpr = result.take(); - baseObjectIsPointer = false; - baseQuals = baseObjectExpr->getType().getQualifiers(); - - // Case 2: the base of the indirect field is a field and the user - // wrote a member expression. - } else if (baseObjectExpr) { - // The caller provided the base object expression. Determine - // whether its a pointer and whether it adds any qualifiers to the - // anonymous struct/union fields we're looking into. - QualType objectType = baseObjectExpr->getType(); - - if (const PointerType *ptr = objectType->getAs<PointerType>()) { - baseObjectIsPointer = true; - objectType = ptr->getPointeeType(); - } else { - baseObjectIsPointer = false; - } - baseQuals = objectType.getQualifiers(); - - // Case 3: the base of the indirect field is a field and we should - // build an implicit member access. - } else { - // We've found a member of an anonymous struct/union that is - // inside a non-anonymous struct/union, so in a well-formed - // program our base object expression is "this". - QualType ThisTy = getAndCaptureCurrentThisType(); - if (ThisTy.isNull()) { - Diag(loc, diag::err_invalid_member_use_in_static_method) - << indirectField->getDeclName(); - return ExprError(); - } - - // Our base object expression is "this". - baseObjectExpr = - new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true); - baseObjectIsPointer = true; - baseQuals = ThisTy->castAs<PointerType>()->getPointeeType().getQualifiers(); - } - - // Build the implicit member references to the field of the - // anonymous struct/union. - Expr *result = baseObjectExpr; - IndirectFieldDecl::chain_iterator - FI = indirectField->chain_begin(), FEnd = indirectField->chain_end(); - - // Build the first member access in the chain with full information. - if (!baseVariable) { - FieldDecl *field = cast<FieldDecl>(*FI); - - // FIXME: use the real found-decl info! - DeclAccessPair foundDecl = DeclAccessPair::make(field, field->getAccess()); - - // Make a nameInfo that properly uses the anonymous name. - DeclarationNameInfo memberNameInfo(field->getDeclName(), loc); - - result = BuildFieldReferenceExpr(*this, result, baseObjectIsPointer, - EmptySS, field, foundDecl, - memberNameInfo).take(); - baseObjectIsPointer = false; - - // FIXME: check qualified member access - } - - // In all cases, we should now skip the first declaration in the chain. - ++FI; - - while (FI != FEnd) { - FieldDecl *field = cast<FieldDecl>(*FI++); - - // FIXME: these are somewhat meaningless - DeclarationNameInfo memberNameInfo(field->getDeclName(), loc); - DeclAccessPair foundDecl = DeclAccessPair::make(field, field->getAccess()); - - result = BuildFieldReferenceExpr(*this, result, /*isarrow*/ false, - (FI == FEnd? SS : EmptySS), field, - foundDecl, memberNameInfo) - .take(); - } - - return Owned(result); -} - /// Decomposes the given name into a DeclarationNameInfo, its location, and /// possibly a list of template arguments. /// @@ -1399,215 +1291,30 @@ Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, /// This actually loses a lot of source location information for /// non-standard name kinds; we should consider preserving that in /// some way. -static void DecomposeUnqualifiedId(Sema &SemaRef, - const UnqualifiedId &Id, - TemplateArgumentListInfo &Buffer, - DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *&TemplateArgs) { +void Sema::DecomposeUnqualifiedId(const UnqualifiedId &Id, + TemplateArgumentListInfo &Buffer, + DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *&TemplateArgs) { if (Id.getKind() == UnqualifiedId::IK_TemplateId) { Buffer.setLAngleLoc(Id.TemplateId->LAngleLoc); Buffer.setRAngleLoc(Id.TemplateId->RAngleLoc); - ASTTemplateArgsPtr TemplateArgsPtr(SemaRef, + ASTTemplateArgsPtr TemplateArgsPtr(*this, Id.TemplateId->getTemplateArgs(), Id.TemplateId->NumArgs); - SemaRef.translateTemplateArguments(TemplateArgsPtr, Buffer); + translateTemplateArguments(TemplateArgsPtr, Buffer); TemplateArgsPtr.release(); TemplateName TName = Id.TemplateId->Template.get(); SourceLocation TNameLoc = Id.TemplateId->TemplateNameLoc; - NameInfo = SemaRef.Context.getNameForTemplate(TName, TNameLoc); + NameInfo = Context.getNameForTemplate(TName, TNameLoc); TemplateArgs = &Buffer; } else { - NameInfo = SemaRef.GetNameFromUnqualifiedId(Id); + NameInfo = GetNameFromUnqualifiedId(Id); TemplateArgs = 0; } } -/// Determines if the given class is provably not derived from all of -/// the prospective base classes. -static bool IsProvablyNotDerivedFrom(Sema &SemaRef, - CXXRecordDecl *Record, - const llvm::SmallPtrSet<CXXRecordDecl*, 4> &Bases) { - if (Bases.count(Record->getCanonicalDecl())) - return false; - - RecordDecl *RD = Record->getDefinition(); - if (!RD) return false; - Record = cast<CXXRecordDecl>(RD); - - for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(), - E = Record->bases_end(); I != E; ++I) { - CanQualType BaseT = SemaRef.Context.getCanonicalType((*I).getType()); - CanQual<RecordType> BaseRT = BaseT->getAs<RecordType>(); - if (!BaseRT) return false; - - CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl()); - if (!IsProvablyNotDerivedFrom(SemaRef, BaseRecord, Bases)) - return false; - } - - return true; -} - -enum IMAKind { - /// The reference is definitely not an instance member access. - IMA_Static, - - /// The reference may be an implicit instance member access. - IMA_Mixed, - - /// The reference may be to an instance member, but it is invalid if - /// so, because the context is not an instance method. - IMA_Mixed_StaticContext, - - /// The reference may be to an instance member, but it is invalid if - /// so, because the context is from an unrelated class. - IMA_Mixed_Unrelated, - - /// The reference is definitely an implicit instance member access. - IMA_Instance, - - /// The reference may be to an unresolved using declaration. - IMA_Unresolved, - - /// The reference may be to an unresolved using declaration and the - /// context is not an instance method. - IMA_Unresolved_StaticContext, - - /// All possible referrents are instance members and the current - /// context is not an instance method. - IMA_Error_StaticContext, - - /// All possible referrents are instance members of an unrelated - /// class. - IMA_Error_Unrelated -}; - -/// The given lookup names class member(s) and is not being used for -/// an address-of-member expression. Classify the type of access -/// according to whether it's possible that this reference names an -/// instance member. This is best-effort; it is okay to -/// conservatively answer "yes", in which case some errors will simply -/// not be caught until template-instantiation. -static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, - Scope *CurScope, - const LookupResult &R) { - assert(!R.empty() && (*R.begin())->isCXXClassMember()); - - DeclContext *DC = SemaRef.getFunctionLevelDeclContext(); - - bool isStaticContext = - (!isa<CXXMethodDecl>(DC) || - cast<CXXMethodDecl>(DC)->isStatic()); - - // C++0x [expr.prim]p4: - // Otherwise, if a member-declarator declares a non-static data member - // of a class X, the expression this is a prvalue of type "pointer to X" - // within the optional brace-or-equal-initializer. - if (CurScope->getFlags() & Scope::ThisScope) - isStaticContext = false; - - if (R.isUnresolvableResult()) - return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved; - - // Collect all the declaring classes of instance members we find. - bool hasNonInstance = false; - bool hasField = false; - llvm::SmallPtrSet<CXXRecordDecl*, 4> Classes; - for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { - NamedDecl *D = *I; - - if (D->isCXXInstanceMember()) { - if (dyn_cast<FieldDecl>(D)) - hasField = true; - - CXXRecordDecl *R = cast<CXXRecordDecl>(D->getDeclContext()); - Classes.insert(R->getCanonicalDecl()); - } - else - hasNonInstance = true; - } - - // If we didn't find any instance members, it can't be an implicit - // member reference. - if (Classes.empty()) - return IMA_Static; - - // If the current context is not an instance method, it can't be - // an implicit member reference. - if (isStaticContext) { - if (hasNonInstance) - return IMA_Mixed_StaticContext; - - if (SemaRef.getLangOptions().CPlusPlus0x && hasField) { - // C++0x [expr.prim.general]p10: - // An id-expression that denotes a non-static data member or non-static - // member function of a class can only be used: - // (...) - // - if that id-expression denotes a non-static data member and it appears in an unevaluated operand. - const Sema::ExpressionEvaluationContextRecord& record = SemaRef.ExprEvalContexts.back(); - bool isUnevaluatedExpression = record.Context == Sema::Unevaluated; - if (isUnevaluatedExpression) - return IMA_Mixed_StaticContext; - } - - return IMA_Error_StaticContext; - } - - CXXRecordDecl *contextClass; - if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC)) - contextClass = MD->getParent()->getCanonicalDecl(); - else - contextClass = cast<CXXRecordDecl>(DC); - - // [class.mfct.non-static]p3: - // ...is used in the body of a non-static member function of class X, - // if name lookup (3.4.1) resolves the name in the id-expression to a - // non-static non-type member of some class C [...] - // ...if C is not X or a base class of X, the class member access expression - // is ill-formed. - if (R.getNamingClass() && - contextClass != R.getNamingClass()->getCanonicalDecl() && - contextClass->isProvablyNotDerivedFrom(R.getNamingClass())) - return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); - - // If we can prove that the current context is unrelated to all the - // declaring classes, it can't be an implicit member reference (in - // which case it's an error if any of those members are selected). - if (IsProvablyNotDerivedFrom(SemaRef, contextClass, Classes)) - return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); - - return (hasNonInstance ? IMA_Mixed : IMA_Instance); -} - -/// Diagnose a reference to a field with no object available. -static void DiagnoseInstanceReference(Sema &SemaRef, - const CXXScopeSpec &SS, - NamedDecl *rep, - const DeclarationNameInfo &nameInfo) { - SourceLocation Loc = nameInfo.getLoc(); - SourceRange Range(Loc); - if (SS.isSet()) Range.setBegin(SS.getRange().getBegin()); - - if (isa<FieldDecl>(rep) || isa<IndirectFieldDecl>(rep)) { - if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(SemaRef.CurContext)) { - if (MD->isStatic()) { - // "invalid use of member 'x' in static member function" - SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method) - << Range << nameInfo.getName(); - return; - } - } - - SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use) - << nameInfo.getName() << Range; - return; - } - - SemaRef.Diag(Loc, diag::err_member_call_without_object) << Range; -} - /// Diagnose an empty lookup. /// /// \return false if new lookup candidates were found @@ -1690,39 +1397,43 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, } // We didn't find anything, so try to correct for a typo. - DeclarationName Corrected; - if (S && (Corrected = CorrectTypo(R, S, &SS, 0, false, CTC))) { - if (!R.empty()) { - if (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin())) { + TypoCorrection Corrected; + if (S && (Corrected = CorrectTypo(R.getLookupNameInfo(), R.getLookupKind(), + S, &SS, NULL, false, CTC))) { + std::string CorrectedStr(Corrected.getAsString(getLangOptions())); + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions())); + R.setLookupName(Corrected.getCorrection()); + + if (NamedDecl *ND = Corrected.getCorrectionDecl()) { + R.addDecl(ND); + if (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND)) { if (SS.isEmpty()) - Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName() - << FixItHint::CreateReplacement(R.getNameLoc(), - R.getLookupName().getAsString()); + Diag(R.getNameLoc(), diagnostic_suggest) << Name << CorrectedQuotedStr + << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); else Diag(R.getNameLoc(), diag::err_no_member_suggest) - << Name << computeDeclContext(SS, false) << R.getLookupName() + << Name << computeDeclContext(SS, false) << CorrectedQuotedStr << SS.getRange() - << FixItHint::CreateReplacement(R.getNameLoc(), - R.getLookupName().getAsString()); - if (NamedDecl *ND = R.getAsSingle<NamedDecl>()) + << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); + if (ND) Diag(ND->getLocation(), diag::note_previous_decl) - << ND->getDeclName(); + << CorrectedQuotedStr; // Tell the callee to try to recover. return false; } - if (isa<TypeDecl>(*R.begin()) || isa<ObjCInterfaceDecl>(*R.begin())) { + if (isa<TypeDecl>(ND) || isa<ObjCInterfaceDecl>(ND)) { // FIXME: If we ended up with a typo for a type name or // Objective-C class name, we're in trouble because the parser // is in the wrong place to recover. Suggest the typo // correction, but don't make it a fix-it since we're not going // to recover well anyway. if (SS.isEmpty()) - Diag(R.getNameLoc(), diagnostic_suggest) << Name << R.getLookupName(); + Diag(R.getNameLoc(), diagnostic_suggest) << Name << CorrectedQuotedStr; else Diag(R.getNameLoc(), diag::err_no_member_suggest) - << Name << computeDeclContext(SS, false) << R.getLookupName() + << Name << computeDeclContext(SS, false) << CorrectedQuotedStr << SS.getRange(); // Don't try to recover; it won't work. @@ -1732,15 +1443,15 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, // FIXME: We found a keyword. Suggest it, but don't provide a fix-it // because we aren't able to recover. if (SS.isEmpty()) - Diag(R.getNameLoc(), diagnostic_suggest) << Name << Corrected; + Diag(R.getNameLoc(), diagnostic_suggest) << Name << CorrectedQuotedStr; else Diag(R.getNameLoc(), diag::err_no_member_suggest) - << Name << computeDeclContext(SS, false) << Corrected + << Name << computeDeclContext(SS, false) << CorrectedQuotedStr << SS.getRange(); return true; } - R.clear(); } + R.clear(); // Emit a special diagnostic for failed member lookups. // FIXME: computing the declaration context might fail here (?) @@ -1856,7 +1567,7 @@ ExprResult Sema::ActOnIdExpression(Scope *S, // Decompose the UnqualifiedId into the following data. DeclarationNameInfo NameInfo; const TemplateArgumentListInfo *TemplateArgs; - DecomposeUnqualifiedId(*this, Id, TemplateArgsBuffer, NameInfo, TemplateArgs); + DecomposeUnqualifiedId(Id, TemplateArgsBuffer, NameInfo, TemplateArgs); DeclarationName Name = NameInfo.getName(); IdentifierInfo *II = Name.getAsIdentifierInfo(); @@ -1892,7 +1603,9 @@ ExprResult Sema::ActOnIdExpression(Scope *S, bool IvarLookupFollowUp = false; // Perform the required lookup. - LookupResult R(*this, NameInfo, LookupOrdinaryName); + LookupResult R(*this, NameInfo, + (Id.getKind() == UnqualifiedId::IK_ImplicitSelfParam) + ? LookupObjCImplicitSelfParam : LookupOrdinaryName); if (TemplateArgs) { // Lookup the template name again to correctly establish the context in // which it was found. This is really unfortunate as we already did the @@ -2032,38 +1745,6 @@ ExprResult Sema::ActOnIdExpression(Scope *S, return BuildDeclarationNameExpr(SS, R, ADL); } -/// Builds an expression which might be an implicit member expression. -ExprResult -Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs) { - switch (ClassifyImplicitMemberAccess(*this, CurScope, R)) { - case IMA_Instance: - return BuildImplicitMemberExpr(SS, R, TemplateArgs, true); - - case IMA_Mixed: - case IMA_Mixed_Unrelated: - case IMA_Unresolved: - return BuildImplicitMemberExpr(SS, R, TemplateArgs, false); - - case IMA_Static: - case IMA_Mixed_StaticContext: - case IMA_Unresolved_StaticContext: - if (TemplateArgs) - return BuildTemplateIdExpr(SS, R, false, *TemplateArgs); - return BuildDeclarationNameExpr(SS, R, false); - - case IMA_Error_StaticContext: - case IMA_Error_Unrelated: - DiagnoseInstanceReference(*this, SS, R.getRepresentativeDecl(), - R.getLookupNameInfo()); - return ExprError(); - } - - llvm_unreachable("unexpected instance member access kind"); - return ExprError(); -} - /// BuildQualifiedDeclarationNameExpr - Build a C++ qualified /// declaration name, generally during template instantiation. /// There's a large number of things which don't need to be done along @@ -2155,6 +1836,7 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S, IdentifierInfo &II = Context.Idents.get("self"); UnqualifiedId SelfName; SelfName.setIdentifier(&II, SourceLocation()); + SelfName.setKind(UnqualifiedId::IK_ImplicitSelfParam); CXXScopeSpec SelfScopeSpec; ExprResult SelfExpr = ActOnIdExpression(S, SelfScopeSpec, SelfName, false, false); @@ -2166,27 +1848,6 @@ Sema::LookupInObjCMethod(LookupResult &Lookup, Scope *S, return ExprError(); MarkDeclarationReferenced(Loc, IV); - Expr *base = SelfExpr.take(); - base = base->IgnoreParenImpCasts(); - if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(base)) { - const NamedDecl *ND = DE->getDecl(); - if (!isa<ImplicitParamDecl>(ND)) { - // relax the rule such that it is allowed to have a shadow 'self' - // where stand-alone ivar can be found in this 'self' object. - // This is to match gcc's behavior. - ObjCInterfaceDecl *selfIFace = 0; - if (const ObjCObjectPointerType *OPT = - base->getType()->getAsObjCInterfacePointerType()) - selfIFace = OPT->getInterfaceDecl(); - if (!selfIFace || - !selfIFace->lookupInstanceVariable(IV->getIdentifier())) { - Diag(Loc, diag::error_implicit_ivar_access) - << IV->getDeclName(); - Diag(ND->getLocation(), diag::note_declared_at); - return ExprError(); - } - } - } return Owned(new (Context) ObjCIvarRefExpr(IV, IV->getType(), Loc, SelfExpr.take(), true, true)); @@ -2385,120 +2046,6 @@ Sema::PerformObjectMemberConversion(Expr *From, VK, &BasePath); } -/// \brief Build a MemberExpr AST node. -static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow, - const CXXScopeSpec &SS, ValueDecl *Member, - DeclAccessPair FoundDecl, - const DeclarationNameInfo &MemberNameInfo, - QualType Ty, - ExprValueKind VK, ExprObjectKind OK, - const TemplateArgumentListInfo *TemplateArgs = 0) { - return MemberExpr::Create(C, Base, isArrow, SS.getWithLocInContext(C), - Member, FoundDecl, MemberNameInfo, - TemplateArgs, Ty, VK, OK); -} - -static ExprResult -BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow, - const CXXScopeSpec &SS, FieldDecl *Field, - DeclAccessPair FoundDecl, - const DeclarationNameInfo &MemberNameInfo) { - // x.a is an l-value if 'a' has a reference type. Otherwise: - // x.a is an l-value/x-value/pr-value if the base is (and note - // that *x is always an l-value), except that if the base isn't - // an ordinary object then we must have an rvalue. - ExprValueKind VK = VK_LValue; - ExprObjectKind OK = OK_Ordinary; - if (!IsArrow) { - if (BaseExpr->getObjectKind() == OK_Ordinary) - VK = BaseExpr->getValueKind(); - else - VK = VK_RValue; - } - if (VK != VK_RValue && Field->isBitField()) - OK = OK_BitField; - - // Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref] - QualType MemberType = Field->getType(); - if (const ReferenceType *Ref = MemberType->getAs<ReferenceType>()) { - MemberType = Ref->getPointeeType(); - VK = VK_LValue; - } else { - QualType BaseType = BaseExpr->getType(); - if (IsArrow) BaseType = BaseType->getAs<PointerType>()->getPointeeType(); - - Qualifiers BaseQuals = BaseType.getQualifiers(); - - // GC attributes are never picked up by members. - BaseQuals.removeObjCGCAttr(); - - // CVR attributes from the base are picked up by members, - // except that 'mutable' members don't pick up 'const'. - if (Field->isMutable()) BaseQuals.removeConst(); - - Qualifiers MemberQuals - = S.Context.getCanonicalType(MemberType).getQualifiers(); - - // TR 18037 does not allow fields to be declared with address spaces. - assert(!MemberQuals.hasAddressSpace()); - - Qualifiers Combined = BaseQuals + MemberQuals; - if (Combined != MemberQuals) - MemberType = S.Context.getQualifiedType(MemberType, Combined); - } - - S.MarkDeclarationReferenced(MemberNameInfo.getLoc(), Field); - ExprResult Base = - S.PerformObjectMemberConversion(BaseExpr, SS.getScopeRep(), - FoundDecl, Field); - if (Base.isInvalid()) - return ExprError(); - return S.Owned(BuildMemberExpr(S.Context, Base.take(), IsArrow, SS, - Field, FoundDecl, MemberNameInfo, - MemberType, VK, OK)); -} - -/// Builds an implicit member access expression. The current context -/// is known to be an instance method, and the given unqualified lookup -/// set is known to contain only instance members, at least one of which -/// is from an appropriate type. -ExprResult -Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs, - bool IsKnownInstance) { - assert(!R.empty() && !R.isAmbiguous()); - - SourceLocation loc = R.getNameLoc(); - - // We may have found a field within an anonymous union or struct - // (C++ [class.union]). - // FIXME: template-ids inside anonymous structs? - if (IndirectFieldDecl *FD = R.getAsSingle<IndirectFieldDecl>()) - return BuildAnonymousStructUnionMemberReference(SS, R.getNameLoc(), FD); - - // If this is known to be an instance access, go ahead and build an - // implicit 'this' expression now. - // 'this' expression now. - QualType ThisTy = getAndCaptureCurrentThisType(); - assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'"); - - Expr *baseExpr = 0; // null signifies implicit access - if (IsKnownInstance) { - SourceLocation Loc = R.getNameLoc(); - if (SS.getRange().isValid()) - Loc = SS.getRange().getBegin(); - baseExpr = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/true); - } - - return BuildMemberReferenceExpr(baseExpr, ThisTy, - /*OpLoc*/ SourceLocation(), - /*IsArrow*/ true, - SS, - /*FirstQualifierInScope*/ 0, - R, TemplateArgs); -} - bool Sema::UseArgumentDependentLookup(const CXXScopeSpec &SS, const LookupResult &R, bool HasTrailingLParen) { @@ -3160,6 +2707,20 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *Op, Op->getSourceRange(), ExprKind)) return true; + if (ExprKind == UETT_SizeOf) { + if (DeclRefExpr *DeclRef = dyn_cast<DeclRefExpr>(Op->IgnoreParens())) { + if (ParmVarDecl *PVD = dyn_cast<ParmVarDecl>(DeclRef->getFoundDecl())) { + QualType OType = PVD->getOriginalType(); + QualType Type = PVD->getType(); + if (Type->isPointerType() && OType->isArrayType()) { + Diag(Op->getExprLoc(), diag::warn_sizeof_array_param) + << Type << OType; + Diag(PVD->getLocation(), diag::note_declared_at); + } + } + } + } + return false; } @@ -3274,6 +2835,12 @@ Sema::CreateUnaryExprOrTypeTraitExpr(TypeSourceInfo *TInfo, ExprResult Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc, UnaryExprOrTypeTrait ExprKind) { + ExprResult PE = CheckPlaceholderExpr(E); + if (PE.isInvalid()) + return ExprError(); + + E = PE.get(); + // Verify that the operand is valid. bool isInvalid = false; if (E->isTypeDependent()) { @@ -3285,10 +2852,6 @@ Sema::CreateUnaryExprOrTypeTraitExpr(Expr *E, SourceLocation OpLoc, } else if (E->getBitField()) { // C99 6.5.3.4p1. Diag(E->getExprLoc(), diag::err_sizeof_alignof_bitfield) << 0; isInvalid = true; - } else if (E->getType()->isPlaceholderType()) { - ExprResult PE = CheckPlaceholderExpr(E); - if (PE.isInvalid()) return ExprError(); - return CreateUnaryExprOrTypeTraitExpr(PE.take(), OpLoc, ExprKind); } else { isInvalid = CheckUnaryExprOrTypeTraitOperand(E, UETT_SizeOf); } @@ -3372,19 +2935,6 @@ Sema::ActOnPostfixUnaryOp(Scope *S, SourceLocation OpLoc, return BuildUnaryOp(S, OpLoc, Opc, Input); } -/// Expressions of certain arbitrary types are forbidden by C from -/// having l-value type. These are: -/// - 'void', but not qualified void -/// - function types -/// -/// The exact rule here is C99 6.3.2.1: -/// An lvalue is an expression with an object type or an incomplete -/// type other than void. -static bool IsCForbiddenLValueType(ASTContext &C, QualType T) { - return ((T->isVoidType() && !T.hasQualifiers()) || - T->isFunctionType()); -} - ExprResult Sema::ActOnArraySubscriptExpr(Scope *S, Expr *Base, SourceLocation LLoc, Expr *Idx, SourceLocation RLoc) { @@ -3528,7 +3078,7 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, if (ResultType->isVoidType() && !getLangOptions().CPlusPlus) { // GNU extension: subscripting on pointer to void - Diag(LLoc, diag::ext_gnu_void_ptr) + Diag(LLoc, diag::ext_gnu_subscript_void_type) << BaseExpr->getSourceRange(); // C forbids expressions of unqualified void type from being l-values. @@ -3548,1109 +3098,12 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc, } assert(VK == VK_RValue || LangOpts.CPlusPlus || - !IsCForbiddenLValueType(Context, ResultType)); + !ResultType.isCForbiddenLValueType()); return Owned(new (Context) ArraySubscriptExpr(LHSExp, RHSExp, ResultType, VK, OK, RLoc)); } -/// Check an ext-vector component access expression. -/// -/// VK should be set in advance to the value kind of the base -/// expression. -static QualType -CheckExtVectorComponent(Sema &S, QualType baseType, ExprValueKind &VK, - SourceLocation OpLoc, const IdentifierInfo *CompName, - SourceLocation CompLoc) { - // FIXME: Share logic with ExtVectorElementExpr::containsDuplicateElements, - // see FIXME there. - // - // FIXME: This logic can be greatly simplified by splitting it along - // halving/not halving and reworking the component checking. - const ExtVectorType *vecType = baseType->getAs<ExtVectorType>(); - - // The vector accessor can't exceed the number of elements. - const char *compStr = CompName->getNameStart(); - - // This flag determines whether or not the component is one of the four - // special names that indicate a subset of exactly half the elements are - // to be selected. - bool HalvingSwizzle = false; - - // This flag determines whether or not CompName has an 's' char prefix, - // indicating that it is a string of hex values to be used as vector indices. - bool HexSwizzle = *compStr == 's' || *compStr == 'S'; - - bool HasRepeated = false; - bool HasIndex[16] = {}; - - int Idx; - - // Check that we've found one of the special components, or that the component - // names must come from the same set. - if (!strcmp(compStr, "hi") || !strcmp(compStr, "lo") || - !strcmp(compStr, "even") || !strcmp(compStr, "odd")) { - HalvingSwizzle = true; - } else if (!HexSwizzle && - (Idx = vecType->getPointAccessorIdx(*compStr)) != -1) { - do { - if (HasIndex[Idx]) HasRepeated = true; - HasIndex[Idx] = true; - compStr++; - } while (*compStr && (Idx = vecType->getPointAccessorIdx(*compStr)) != -1); - } else { - if (HexSwizzle) compStr++; - while ((Idx = vecType->getNumericAccessorIdx(*compStr)) != -1) { - if (HasIndex[Idx]) HasRepeated = true; - HasIndex[Idx] = true; - compStr++; - } - } - - if (!HalvingSwizzle && *compStr) { - // We didn't get to the end of the string. This means the component names - // didn't come from the same set *or* we encountered an illegal name. - S.Diag(OpLoc, diag::err_ext_vector_component_name_illegal) - << llvm::StringRef(compStr, 1) << SourceRange(CompLoc); - return QualType(); - } - - // Ensure no component accessor exceeds the width of the vector type it - // operates on. - if (!HalvingSwizzle) { - compStr = CompName->getNameStart(); - - if (HexSwizzle) - compStr++; - - while (*compStr) { - if (!vecType->isAccessorWithinNumElements(*compStr++)) { - S.Diag(OpLoc, diag::err_ext_vector_component_exceeds_length) - << baseType << SourceRange(CompLoc); - return QualType(); - } - } - } - - // The component accessor looks fine - now we need to compute the actual type. - // The vector type is implied by the component accessor. For example, - // vec4.b is a float, vec4.xy is a vec2, vec4.rgb is a vec3, etc. - // vec4.s0 is a float, vec4.s23 is a vec3, etc. - // vec4.hi, vec4.lo, vec4.e, and vec4.o all return vec2. - unsigned CompSize = HalvingSwizzle ? (vecType->getNumElements() + 1) / 2 - : CompName->getLength(); - if (HexSwizzle) - CompSize--; - - if (CompSize == 1) - return vecType->getElementType(); - - if (HasRepeated) VK = VK_RValue; - - QualType VT = S.Context.getExtVectorType(vecType->getElementType(), CompSize); - // Now look up the TypeDefDecl from the vector type. Without this, - // diagostics look bad. We want extended vector types to appear built-in. - for (unsigned i = 0, E = S.ExtVectorDecls.size(); i != E; ++i) { - if (S.ExtVectorDecls[i]->getUnderlyingType() == VT) - return S.Context.getTypedefType(S.ExtVectorDecls[i]); - } - return VT; // should never get here (a typedef type should always be found). -} - -static Decl *FindGetterSetterNameDeclFromProtocolList(const ObjCProtocolDecl*PDecl, - IdentifierInfo *Member, - const Selector &Sel, - ASTContext &Context) { - if (Member) - if (ObjCPropertyDecl *PD = PDecl->FindPropertyDeclaration(Member)) - return PD; - if (ObjCMethodDecl *OMD = PDecl->getInstanceMethod(Sel)) - return OMD; - - for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), - E = PDecl->protocol_end(); I != E; ++I) { - if (Decl *D = FindGetterSetterNameDeclFromProtocolList(*I, Member, Sel, - Context)) - return D; - } - return 0; -} - -static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy, - IdentifierInfo *Member, - const Selector &Sel, - ASTContext &Context) { - // Check protocols on qualified interfaces. - Decl *GDecl = 0; - for (ObjCObjectPointerType::qual_iterator I = QIdTy->qual_begin(), - E = QIdTy->qual_end(); I != E; ++I) { - if (Member) - if (ObjCPropertyDecl *PD = (*I)->FindPropertyDeclaration(Member)) { - GDecl = PD; - break; - } - // Also must look for a getter or setter name which uses property syntax. - if (ObjCMethodDecl *OMD = (*I)->getInstanceMethod(Sel)) { - GDecl = OMD; - break; - } - } - if (!GDecl) { - for (ObjCObjectPointerType::qual_iterator I = QIdTy->qual_begin(), - E = QIdTy->qual_end(); I != E; ++I) { - // Search in the protocol-qualifier list of current protocol. - GDecl = FindGetterSetterNameDeclFromProtocolList(*I, Member, Sel, - Context); - if (GDecl) - return GDecl; - } - } - return GDecl; -} - -ExprResult -Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, - bool IsArrow, SourceLocation OpLoc, - const CXXScopeSpec &SS, - NamedDecl *FirstQualifierInScope, - const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs) { - // Even in dependent contexts, try to diagnose base expressions with - // obviously wrong types, e.g.: - // - // T* t; - // t.f; - // - // In Obj-C++, however, the above expression is valid, since it could be - // accessing the 'f' property if T is an Obj-C interface. The extra check - // allows this, while still reporting an error if T is a struct pointer. - if (!IsArrow) { - const PointerType *PT = BaseType->getAs<PointerType>(); - if (PT && (!getLangOptions().ObjC1 || - PT->getPointeeType()->isRecordType())) { - assert(BaseExpr && "cannot happen with implicit member accesses"); - Diag(NameInfo.getLoc(), diag::err_typecheck_member_reference_struct_union) - << BaseType << BaseExpr->getSourceRange(); - return ExprError(); - } - } - - assert(BaseType->isDependentType() || - NameInfo.getName().isDependentName() || - isDependentScopeSpecifier(SS)); - - // Get the type being accessed in BaseType. If this is an arrow, the BaseExpr - // must have pointer type, and the accessed type is the pointee. - return Owned(CXXDependentScopeMemberExpr::Create(Context, BaseExpr, BaseType, - IsArrow, OpLoc, - SS.getWithLocInContext(Context), - FirstQualifierInScope, - NameInfo, TemplateArgs)); -} - -/// We know that the given qualified member reference points only to -/// declarations which do not belong to the static type of the base -/// expression. Diagnose the problem. -static void DiagnoseQualifiedMemberReference(Sema &SemaRef, - Expr *BaseExpr, - QualType BaseType, - const CXXScopeSpec &SS, - NamedDecl *rep, - const DeclarationNameInfo &nameInfo) { - // If this is an implicit member access, use a different set of - // diagnostics. - if (!BaseExpr) - return DiagnoseInstanceReference(SemaRef, SS, rep, nameInfo); - - SemaRef.Diag(nameInfo.getLoc(), diag::err_qualified_member_of_unrelated) - << SS.getRange() << rep << BaseType; -} - -// Check whether the declarations we found through a nested-name -// specifier in a member expression are actually members of the base -// type. The restriction here is: -// -// C++ [expr.ref]p2: -// ... In these cases, the id-expression shall name a -// member of the class or of one of its base classes. -// -// So it's perfectly legitimate for the nested-name specifier to name -// an unrelated class, and for us to find an overload set including -// decls from classes which are not superclasses, as long as the decl -// we actually pick through overload resolution is from a superclass. -bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, - QualType BaseType, - const CXXScopeSpec &SS, - const LookupResult &R) { - const RecordType *BaseRT = BaseType->getAs<RecordType>(); - if (!BaseRT) { - // We can't check this yet because the base type is still - // dependent. - assert(BaseType->isDependentType()); - return false; - } - CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl()); - - for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { - // If this is an implicit member reference and we find a - // non-instance member, it's not an error. - if (!BaseExpr && !(*I)->isCXXInstanceMember()) - return false; - - // Note that we use the DC of the decl, not the underlying decl. - DeclContext *DC = (*I)->getDeclContext(); - while (DC->isTransparentContext()) - DC = DC->getParent(); - - if (!DC->isRecord()) - continue; - - llvm::SmallPtrSet<CXXRecordDecl*,4> MemberRecord; - MemberRecord.insert(cast<CXXRecordDecl>(DC)->getCanonicalDecl()); - - if (!IsProvablyNotDerivedFrom(*this, BaseRecord, MemberRecord)) - return false; - } - - DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS, - R.getRepresentativeDecl(), - R.getLookupNameInfo()); - return true; -} - -static bool -LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, - SourceRange BaseRange, const RecordType *RTy, - SourceLocation OpLoc, CXXScopeSpec &SS, - bool HasTemplateArgs) { - RecordDecl *RDecl = RTy->getDecl(); - if (SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), - SemaRef.PDiag(diag::err_typecheck_incomplete_tag) - << BaseRange)) - return true; - - if (HasTemplateArgs) { - // LookupTemplateName doesn't expect these both to exist simultaneously. - QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); - - bool MOUS; - SemaRef.LookupTemplateName(R, 0, SS, ObjectType, false, MOUS); - return false; - } - - DeclContext *DC = RDecl; - if (SS.isSet()) { - // If the member name was a qualified-id, look into the - // nested-name-specifier. - DC = SemaRef.computeDeclContext(SS, false); - - if (SemaRef.RequireCompleteDeclContext(SS, DC)) { - SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) - << SS.getRange() << DC; - return true; - } - - assert(DC && "Cannot handle non-computable dependent contexts in lookup"); - - if (!isa<TypeDecl>(DC)) { - SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) - << DC << SS.getRange(); - return true; - } - } - - // The record definition is complete, now look up the member. - SemaRef.LookupQualifiedName(R, DC); - - if (!R.empty()) - return false; - - // We didn't find anything with the given name, so try to correct - // for typos. - DeclarationName Name = R.getLookupName(); - if (SemaRef.CorrectTypo(R, 0, &SS, DC, false, Sema::CTC_MemberLookup) && - !R.empty() && - (isa<ValueDecl>(*R.begin()) || isa<FunctionTemplateDecl>(*R.begin()))) { - SemaRef.Diag(R.getNameLoc(), diag::err_no_member_suggest) - << Name << DC << R.getLookupName() << SS.getRange() - << FixItHint::CreateReplacement(R.getNameLoc(), - R.getLookupName().getAsString()); - if (NamedDecl *ND = R.getAsSingle<NamedDecl>()) - SemaRef.Diag(ND->getLocation(), diag::note_previous_decl) - << ND->getDeclName(); - return false; - } else { - R.clear(); - R.setLookupName(Name); - } - - return false; -} - -ExprResult -Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, - SourceLocation OpLoc, bool IsArrow, - CXXScopeSpec &SS, - NamedDecl *FirstQualifierInScope, - const DeclarationNameInfo &NameInfo, - const TemplateArgumentListInfo *TemplateArgs) { - if (BaseType->isDependentType() || - (SS.isSet() && isDependentScopeSpecifier(SS))) - return ActOnDependentMemberExpr(Base, BaseType, - IsArrow, OpLoc, - SS, FirstQualifierInScope, - NameInfo, TemplateArgs); - - LookupResult R(*this, NameInfo, LookupMemberName); - - // Implicit member accesses. - if (!Base) { - QualType RecordTy = BaseType; - if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType(); - if (LookupMemberExprInRecord(*this, R, SourceRange(), - RecordTy->getAs<RecordType>(), - OpLoc, SS, TemplateArgs != 0)) - return ExprError(); - - // Explicit member accesses. - } else { - ExprResult BaseResult = Owned(Base); - ExprResult Result = - LookupMemberExpr(R, BaseResult, IsArrow, OpLoc, - SS, /*ObjCImpDecl*/ 0, TemplateArgs != 0); - - if (BaseResult.isInvalid()) - return ExprError(); - Base = BaseResult.take(); - - if (Result.isInvalid()) { - Owned(Base); - return ExprError(); - } - - if (Result.get()) - return move(Result); - - // LookupMemberExpr can modify Base, and thus change BaseType - BaseType = Base->getType(); - } - - return BuildMemberReferenceExpr(Base, BaseType, - OpLoc, IsArrow, SS, FirstQualifierInScope, - R, TemplateArgs); -} - -ExprResult -Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, - SourceLocation OpLoc, bool IsArrow, - const CXXScopeSpec &SS, - NamedDecl *FirstQualifierInScope, - LookupResult &R, - const TemplateArgumentListInfo *TemplateArgs, - bool SuppressQualifierCheck) { - QualType BaseType = BaseExprType; - if (IsArrow) { - assert(BaseType->isPointerType()); - BaseType = BaseType->getAs<PointerType>()->getPointeeType(); - } - R.setBaseObjectType(BaseType); - - const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo(); - DeclarationName MemberName = MemberNameInfo.getName(); - SourceLocation MemberLoc = MemberNameInfo.getLoc(); - - if (R.isAmbiguous()) - return ExprError(); - - if (R.empty()) { - // Rederive where we looked up. - DeclContext *DC = (SS.isSet() - ? computeDeclContext(SS, false) - : BaseType->getAs<RecordType>()->getDecl()); - - Diag(R.getNameLoc(), diag::err_no_member) - << MemberName << DC - << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); - return ExprError(); - } - - // Diagnose lookups that find only declarations from a non-base - // type. This is possible for either qualified lookups (which may - // have been qualified with an unrelated type) or implicit member - // expressions (which were found with unqualified lookup and thus - // may have come from an enclosing scope). Note that it's okay for - // lookup to find declarations from a non-base type as long as those - // aren't the ones picked by overload resolution. - if ((SS.isSet() || !BaseExpr || - (isa<CXXThisExpr>(BaseExpr) && - cast<CXXThisExpr>(BaseExpr)->isImplicit())) && - !SuppressQualifierCheck && - CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R)) - return ExprError(); - - // Construct an unresolved result if we in fact got an unresolved - // result. - if (R.isOverloadedResult() || R.isUnresolvableResult()) { - // Suppress any lookup-related diagnostics; we'll do these when we - // pick a member. - R.suppressDiagnostics(); - - UnresolvedMemberExpr *MemExpr - = UnresolvedMemberExpr::Create(Context, R.isUnresolvableResult(), - BaseExpr, BaseExprType, - IsArrow, OpLoc, - SS.getWithLocInContext(Context), - MemberNameInfo, - TemplateArgs, R.begin(), R.end()); - - return Owned(MemExpr); - } - - assert(R.isSingleResult()); - DeclAccessPair FoundDecl = R.begin().getPair(); - NamedDecl *MemberDecl = R.getFoundDecl(); - - // FIXME: diagnose the presence of template arguments now. - - // If the decl being referenced had an error, return an error for this - // sub-expr without emitting another error, in order to avoid cascading - // error cases. - if (MemberDecl->isInvalidDecl()) - return ExprError(); - - // Handle the implicit-member-access case. - if (!BaseExpr) { - // If this is not an instance member, convert to a non-member access. - if (!MemberDecl->isCXXInstanceMember()) - return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), MemberDecl); - - SourceLocation Loc = R.getNameLoc(); - if (SS.getRange().isValid()) - Loc = SS.getRange().getBegin(); - BaseExpr = new (Context) CXXThisExpr(Loc, BaseExprType,/*isImplicit=*/true); - } - - bool ShouldCheckUse = true; - if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MemberDecl)) { - // Don't diagnose the use of a virtual member function unless it's - // explicitly qualified. - if (MD->isVirtual() && !SS.isSet()) - ShouldCheckUse = false; - } - - // Check the use of this member. - if (ShouldCheckUse && DiagnoseUseOfDecl(MemberDecl, MemberLoc)) { - Owned(BaseExpr); - return ExprError(); - } - - // Perform a property load on the base regardless of whether we - // actually need it for the declaration. - if (BaseExpr->getObjectKind() == OK_ObjCProperty) { - ExprResult Result = ConvertPropertyForRValue(BaseExpr); - if (Result.isInvalid()) - return ExprError(); - BaseExpr = Result.take(); - } - - if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) - return BuildFieldReferenceExpr(*this, BaseExpr, IsArrow, - SS, FD, FoundDecl, MemberNameInfo); - - if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl)) - // We may have found a field within an anonymous union or struct - // (C++ [class.union]). - return BuildAnonymousStructUnionMemberReference(SS, MemberLoc, FD, - BaseExpr, OpLoc); - - if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) { - MarkDeclarationReferenced(MemberLoc, Var); - return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, - Var, FoundDecl, MemberNameInfo, - Var->getType().getNonReferenceType(), - VK_LValue, OK_Ordinary)); - } - - if (CXXMethodDecl *MemberFn = dyn_cast<CXXMethodDecl>(MemberDecl)) { - ExprValueKind valueKind; - QualType type; - if (MemberFn->isInstance()) { - valueKind = VK_RValue; - type = Context.BoundMemberTy; - } else { - valueKind = VK_LValue; - type = MemberFn->getType(); - } - - MarkDeclarationReferenced(MemberLoc, MemberDecl); - return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, - MemberFn, FoundDecl, MemberNameInfo, - type, valueKind, OK_Ordinary)); - } - assert(!isa<FunctionDecl>(MemberDecl) && "member function not C++ method?"); - - if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) { - MarkDeclarationReferenced(MemberLoc, MemberDecl); - return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, - Enum, FoundDecl, MemberNameInfo, - Enum->getType(), VK_RValue, OK_Ordinary)); - } - - Owned(BaseExpr); - - // We found something that we didn't expect. Complain. - if (isa<TypeDecl>(MemberDecl)) - Diag(MemberLoc, diag::err_typecheck_member_reference_type) - << MemberName << BaseType << int(IsArrow); - else - Diag(MemberLoc, diag::err_typecheck_member_reference_unknown) - << MemberName << BaseType << int(IsArrow); - - Diag(MemberDecl->getLocation(), diag::note_member_declared_here) - << MemberName; - R.suppressDiagnostics(); - return ExprError(); -} - -/// Given that normal member access failed on the given expression, -/// and given that the expression's type involves builtin-id or -/// builtin-Class, decide whether substituting in the redefinition -/// types would be profitable. The redefinition type is whatever -/// this translation unit tried to typedef to id/Class; we store -/// it to the side and then re-use it in places like this. -static bool ShouldTryAgainWithRedefinitionType(Sema &S, ExprResult &base) { - const ObjCObjectPointerType *opty - = base.get()->getType()->getAs<ObjCObjectPointerType>(); - if (!opty) return false; - - const ObjCObjectType *ty = opty->getObjectType(); - - QualType redef; - if (ty->isObjCId()) { - redef = S.Context.ObjCIdRedefinitionType; - } else if (ty->isObjCClass()) { - redef = S.Context.ObjCClassRedefinitionType; - } else { - return false; - } - - // Do the substitution as long as the redefinition type isn't just a - // possibly-qualified pointer to builtin-id or builtin-Class again. - opty = redef->getAs<ObjCObjectPointerType>(); - if (opty && !opty->getObjectType()->getInterface() != 0) - return false; - - base = S.ImpCastExprToType(base.take(), redef, CK_BitCast); - return true; -} - -/// Look up the given member of the given non-type-dependent -/// expression. This can return in one of two ways: -/// * If it returns a sentinel null-but-valid result, the caller will -/// assume that lookup was performed and the results written into -/// the provided structure. It will take over from there. -/// * Otherwise, the returned expression will be produced in place of -/// an ordinary member expression. -/// -/// The ObjCImpDecl bit is a gross hack that will need to be properly -/// fixed for ObjC++. -ExprResult -Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, - bool &IsArrow, SourceLocation OpLoc, - CXXScopeSpec &SS, - Decl *ObjCImpDecl, bool HasTemplateArgs) { - assert(BaseExpr.get() && "no base expression"); - - // Perform default conversions. - BaseExpr = DefaultFunctionArrayConversion(BaseExpr.take()); - - if (IsArrow) { - BaseExpr = DefaultLvalueConversion(BaseExpr.take()); - if (BaseExpr.isInvalid()) - return ExprError(); - } - - QualType BaseType = BaseExpr.get()->getType(); - assert(!BaseType->isDependentType()); - - DeclarationName MemberName = R.getLookupName(); - SourceLocation MemberLoc = R.getNameLoc(); - - // For later type-checking purposes, turn arrow accesses into dot - // accesses. The only access type we support that doesn't follow - // the C equivalence "a->b === (*a).b" is ObjC property accesses, - // and those never use arrows, so this is unaffected. - if (IsArrow) { - if (const PointerType *Ptr = BaseType->getAs<PointerType>()) - BaseType = Ptr->getPointeeType(); - else if (const ObjCObjectPointerType *Ptr - = BaseType->getAs<ObjCObjectPointerType>()) - BaseType = Ptr->getPointeeType(); - else if (BaseType->isRecordType()) { - // Recover from arrow accesses to records, e.g.: - // struct MyRecord foo; - // foo->bar - // This is actually well-formed in C++ if MyRecord has an - // overloaded operator->, but that should have been dealt with - // by now. - Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "."); - IsArrow = false; - } else if (BaseType == Context.BoundMemberTy) { - goto fail; - } else { - Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) - << BaseType << BaseExpr.get()->getSourceRange(); - return ExprError(); - } - } - - // Handle field access to simple records. - if (const RecordType *RTy = BaseType->getAs<RecordType>()) { - if (LookupMemberExprInRecord(*this, R, BaseExpr.get()->getSourceRange(), - RTy, OpLoc, SS, HasTemplateArgs)) - return ExprError(); - - // Returning valid-but-null is how we indicate to the caller that - // the lookup result was filled in. - return Owned((Expr*) 0); - } - - // Handle ivar access to Objective-C objects. - if (const ObjCObjectType *OTy = BaseType->getAs<ObjCObjectType>()) { - IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); - - // There are three cases for the base type: - // - builtin id (qualified or unqualified) - // - builtin Class (qualified or unqualified) - // - an interface - ObjCInterfaceDecl *IDecl = OTy->getInterface(); - if (!IDecl) { - // There's an implicit 'isa' ivar on all objects. - // But we only actually find it this way on objects of type 'id', - // apparently. - if (OTy->isObjCId() && Member->isStr("isa")) - return Owned(new (Context) ObjCIsaExpr(BaseExpr.take(), IsArrow, MemberLoc, - Context.getObjCClassType())); - - if (ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - goto fail; - } - - ObjCInterfaceDecl *ClassDeclared; - ObjCIvarDecl *IV = IDecl->lookupInstanceVariable(Member, ClassDeclared); - - if (!IV) { - // Attempt to correct for typos in ivar names. - LookupResult Res(*this, R.getLookupName(), R.getNameLoc(), - LookupMemberName); - if (CorrectTypo(Res, 0, 0, IDecl, false, - IsArrow ? CTC_ObjCIvarLookup - : CTC_ObjCPropertyLookup) && - (IV = Res.getAsSingle<ObjCIvarDecl>())) { - Diag(R.getNameLoc(), - diag::err_typecheck_member_reference_ivar_suggest) - << IDecl->getDeclName() << MemberName << IV->getDeclName() - << FixItHint::CreateReplacement(R.getNameLoc(), - IV->getNameAsString()); - Diag(IV->getLocation(), diag::note_previous_decl) - << IV->getDeclName(); - } else { - Res.clear(); - Res.setLookupName(Member); - - Diag(MemberLoc, diag::err_typecheck_member_reference_ivar) - << IDecl->getDeclName() << MemberName - << BaseExpr.get()->getSourceRange(); - return ExprError(); - } - } - - // If the decl being referenced had an error, return an error for this - // sub-expr without emitting another error, in order to avoid cascading - // error cases. - if (IV->isInvalidDecl()) - return ExprError(); - - // Check whether we can reference this field. - if (DiagnoseUseOfDecl(IV, MemberLoc)) - return ExprError(); - if (IV->getAccessControl() != ObjCIvarDecl::Public && - IV->getAccessControl() != ObjCIvarDecl::Package) { - ObjCInterfaceDecl *ClassOfMethodDecl = 0; - if (ObjCMethodDecl *MD = getCurMethodDecl()) - ClassOfMethodDecl = MD->getClassInterface(); - else if (ObjCImpDecl && getCurFunctionDecl()) { - // Case of a c-function declared inside an objc implementation. - // FIXME: For a c-style function nested inside an objc implementation - // class, there is no implementation context available, so we pass - // down the context as argument to this routine. Ideally, this context - // need be passed down in the AST node and somehow calculated from the - // AST for a function decl. - if (ObjCImplementationDecl *IMPD = - dyn_cast<ObjCImplementationDecl>(ObjCImpDecl)) - ClassOfMethodDecl = IMPD->getClassInterface(); - else if (ObjCCategoryImplDecl* CatImplClass = - dyn_cast<ObjCCategoryImplDecl>(ObjCImpDecl)) - ClassOfMethodDecl = CatImplClass->getClassInterface(); - } - - if (IV->getAccessControl() == ObjCIvarDecl::Private) { - if (ClassDeclared != IDecl || - ClassOfMethodDecl != ClassDeclared) - Diag(MemberLoc, diag::error_private_ivar_access) - << IV->getDeclName(); - } else if (!IDecl->isSuperClassOf(ClassOfMethodDecl)) - // @protected - Diag(MemberLoc, diag::error_protected_ivar_access) - << IV->getDeclName(); - } - - return Owned(new (Context) ObjCIvarRefExpr(IV, IV->getType(), - MemberLoc, BaseExpr.take(), - IsArrow)); - } - - // Objective-C property access. - const ObjCObjectPointerType *OPT; - if (!IsArrow && (OPT = BaseType->getAs<ObjCObjectPointerType>())) { - // This actually uses the base as an r-value. - BaseExpr = DefaultLvalueConversion(BaseExpr.take()); - if (BaseExpr.isInvalid()) - return ExprError(); - - assert(Context.hasSameUnqualifiedType(BaseType, BaseExpr.get()->getType())); - - IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); - - const ObjCObjectType *OT = OPT->getObjectType(); - - // id, with and without qualifiers. - if (OT->isObjCId()) { - // Check protocols on qualified interfaces. - Selector Sel = PP.getSelectorTable().getNullarySelector(Member); - if (Decl *PMDecl = FindGetterSetterNameDecl(OPT, Member, Sel, Context)) { - if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(PMDecl)) { - // Check the use of this declaration - if (DiagnoseUseOfDecl(PD, MemberLoc)) - return ExprError(); - - QualType T = PD->getType(); - if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) - T = getMessageSendResultType(BaseType, Getter, false, false); - - return Owned(new (Context) ObjCPropertyRefExpr(PD, T, - VK_LValue, - OK_ObjCProperty, - MemberLoc, - BaseExpr.take())); - } - - if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(PMDecl)) { - // Check the use of this method. - if (DiagnoseUseOfDecl(OMD, MemberLoc)) - return ExprError(); - Selector SetterSel = - SelectorTable::constructSetterName(PP.getIdentifierTable(), - PP.getSelectorTable(), Member); - ObjCMethodDecl *SMD = 0; - if (Decl *SDecl = FindGetterSetterNameDecl(OPT, /*Property id*/0, - SetterSel, Context)) - SMD = dyn_cast<ObjCMethodDecl>(SDecl); - QualType PType = getMessageSendResultType(BaseType, OMD, false, - false); - - ExprValueKind VK = VK_LValue; - if (!getLangOptions().CPlusPlus && - IsCForbiddenLValueType(Context, PType)) - VK = VK_RValue; - ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); - - return Owned(new (Context) ObjCPropertyRefExpr(OMD, SMD, PType, - VK, OK, - MemberLoc, BaseExpr.take())); - } - } - // Use of id.member can only be for a property reference. Do not - // use the 'id' redefinition in this case. - if (IsArrow && ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - - return ExprError(Diag(MemberLoc, diag::err_property_not_found) - << MemberName << BaseType); - } - - // 'Class', unqualified only. - if (OT->isObjCClass()) { - // Only works in a method declaration (??!). - ObjCMethodDecl *MD = getCurMethodDecl(); - if (!MD) { - if (ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - - goto fail; - } - - // Also must look for a getter name which uses property syntax. - Selector Sel = PP.getSelectorTable().getNullarySelector(Member); - ObjCInterfaceDecl *IFace = MD->getClassInterface(); - ObjCMethodDecl *Getter; - if ((Getter = IFace->lookupClassMethod(Sel))) { - // Check the use of this method. - if (DiagnoseUseOfDecl(Getter, MemberLoc)) - return ExprError(); - } else - Getter = IFace->lookupPrivateMethod(Sel, false); - // If we found a getter then this may be a valid dot-reference, we - // will look for the matching setter, in case it is needed. - Selector SetterSel = - SelectorTable::constructSetterName(PP.getIdentifierTable(), - PP.getSelectorTable(), Member); - ObjCMethodDecl *Setter = IFace->lookupClassMethod(SetterSel); - if (!Setter) { - // If this reference is in an @implementation, also check for 'private' - // methods. - Setter = IFace->lookupPrivateMethod(SetterSel, false); - } - // Look through local category implementations associated with the class. - if (!Setter) - Setter = IFace->getCategoryClassMethod(SetterSel); - - if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) - return ExprError(); - - if (Getter || Setter) { - QualType PType; - - ExprValueKind VK = VK_LValue; - if (Getter) { - PType = getMessageSendResultType(QualType(OT, 0), Getter, true, - false); - if (!getLangOptions().CPlusPlus && - IsCForbiddenLValueType(Context, PType)) - VK = VK_RValue; - } else { - // Get the expression type from Setter's incoming parameter. - PType = (*(Setter->param_end() -1))->getType(); - } - ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); - - // FIXME: we must check that the setter has property type. - return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, - PType, VK, OK, - MemberLoc, BaseExpr.take())); - } - - if (ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - - return ExprError(Diag(MemberLoc, diag::err_property_not_found) - << MemberName << BaseType); - } - - // Normal property access. - return HandleExprPropertyRefExpr(OPT, BaseExpr.get(), MemberName, MemberLoc, - SourceLocation(), QualType(), false); - } - - // Handle 'field access' to vectors, such as 'V.xx'. - if (BaseType->isExtVectorType()) { - // FIXME: this expr should store IsArrow. - IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); - ExprValueKind VK = (IsArrow ? VK_LValue : BaseExpr.get()->getValueKind()); - QualType ret = CheckExtVectorComponent(*this, BaseType, VK, OpLoc, - Member, MemberLoc); - if (ret.isNull()) - return ExprError(); - - return Owned(new (Context) ExtVectorElementExpr(ret, VK, BaseExpr.take(), - *Member, MemberLoc)); - } - - // Adjust builtin-sel to the appropriate redefinition type if that's - // not just a pointer to builtin-sel again. - if (IsArrow && - BaseType->isSpecificBuiltinType(BuiltinType::ObjCSel) && - !Context.ObjCSelRedefinitionType->isObjCSelType()) { - BaseExpr = ImpCastExprToType(BaseExpr.take(), Context.ObjCSelRedefinitionType, - CK_BitCast); - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - } - - // Failure cases. - fail: - - // Recover from dot accesses to pointers, e.g.: - // type *foo; - // foo.bar - // This is actually well-formed in two cases: - // - 'type' is an Objective C type - // - 'bar' is a pseudo-destructor name which happens to refer to - // the appropriate pointer type - if (const PointerType *Ptr = BaseType->getAs<PointerType>()) { - if (!IsArrow && Ptr->getPointeeType()->isRecordType() && - MemberName.getNameKind() != DeclarationName::CXXDestructorName) { - Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) - << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() - << FixItHint::CreateReplacement(OpLoc, "->"); - - // Recurse as an -> access. - IsArrow = true; - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - } - } - - // If the user is trying to apply -> or . to a function name, it's probably - // because they forgot parentheses to call that function. - QualType ZeroArgCallTy; - UnresolvedSet<4> Overloads; - if (isExprCallable(*BaseExpr.get(), ZeroArgCallTy, Overloads)) { - if (ZeroArgCallTy.isNull()) { - Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) - << (Overloads.size() > 1) << 0 << BaseExpr.get()->getSourceRange(); - UnresolvedSet<2> PlausibleOverloads; - for (OverloadExpr::decls_iterator It = Overloads.begin(), - DeclsEnd = Overloads.end(); It != DeclsEnd; ++It) { - const FunctionDecl *OverloadDecl = cast<FunctionDecl>(*It); - QualType OverloadResultTy = OverloadDecl->getResultType(); - if ((!IsArrow && OverloadResultTy->isRecordType()) || - (IsArrow && OverloadResultTy->isPointerType() && - OverloadResultTy->getPointeeType()->isRecordType())) - PlausibleOverloads.addDecl(It.getDecl()); - } - NoteOverloads(PlausibleOverloads, BaseExpr.get()->getExprLoc()); - return ExprError(); - } - if ((!IsArrow && ZeroArgCallTy->isRecordType()) || - (IsArrow && ZeroArgCallTy->isPointerType() && - ZeroArgCallTy->getPointeeType()->isRecordType())) { - // At this point, we know BaseExpr looks like it's potentially callable - // with 0 arguments, and that it returns something of a reasonable type, - // so we can emit a fixit and carry on pretending that BaseExpr was - // actually a CallExpr. - SourceLocation ParenInsertionLoc = - PP.getLocForEndOfToken(BaseExpr.get()->getLocEnd()); - Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) - << (Overloads.size() > 1) << 1 << BaseExpr.get()->getSourceRange() - << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); - // FIXME: Try this before emitting the fixit, and suppress diagnostics - // while doing so. - ExprResult NewBase = - ActOnCallExpr(0, BaseExpr.take(), ParenInsertionLoc, - MultiExprArg(*this, 0, 0), - ParenInsertionLoc.getFileLocWithOffset(1)); - if (NewBase.isInvalid()) - return ExprError(); - BaseExpr = NewBase; - BaseExpr = DefaultFunctionArrayConversion(BaseExpr.take()); - return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, - ObjCImpDecl, HasTemplateArgs); - } - } - - Diag(MemberLoc, diag::err_typecheck_member_reference_struct_union) - << BaseType << BaseExpr.get()->getSourceRange(); - - return ExprError(); -} - -/// The main callback when the parser finds something like -/// expression . [nested-name-specifier] identifier -/// expression -> [nested-name-specifier] identifier -/// where 'identifier' encompasses a fairly broad spectrum of -/// possibilities, including destructor and operator references. -/// -/// \param OpKind either tok::arrow or tok::period -/// \param HasTrailingLParen whether the next token is '(', which -/// is used to diagnose mis-uses of special members that can -/// only be called -/// \param ObjCImpDecl the current ObjC @implementation decl; -/// this is an ugly hack around the fact that ObjC @implementations -/// aren't properly put in the context chain -ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, - SourceLocation OpLoc, - tok::TokenKind OpKind, - CXXScopeSpec &SS, - UnqualifiedId &Id, - Decl *ObjCImpDecl, - bool HasTrailingLParen) { - if (SS.isSet() && SS.isInvalid()) - return ExprError(); - - // Warn about the explicit constructor calls Microsoft extension. - if (getLangOptions().Microsoft && - Id.getKind() == UnqualifiedId::IK_ConstructorName) - Diag(Id.getSourceRange().getBegin(), - diag::ext_ms_explicit_constructor_call); - - TemplateArgumentListInfo TemplateArgsBuffer; - - // Decompose the name into its component parts. - DeclarationNameInfo NameInfo; - const TemplateArgumentListInfo *TemplateArgs; - DecomposeUnqualifiedId(*this, Id, TemplateArgsBuffer, - NameInfo, TemplateArgs); - - DeclarationName Name = NameInfo.getName(); - bool IsArrow = (OpKind == tok::arrow); - - NamedDecl *FirstQualifierInScope - = (!SS.isSet() ? 0 : FindFirstQualifierInScope(S, - static_cast<NestedNameSpecifier*>(SS.getScopeRep()))); - - // This is a postfix expression, so get rid of ParenListExprs. - ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base); - if (Result.isInvalid()) return ExprError(); - Base = Result.take(); - - if (Base->getType()->isDependentType() || Name.isDependentName() || - isDependentScopeSpecifier(SS)) { - Result = ActOnDependentMemberExpr(Base, Base->getType(), - IsArrow, OpLoc, - SS, FirstQualifierInScope, - NameInfo, TemplateArgs); - } else { - LookupResult R(*this, NameInfo, LookupMemberName); - ExprResult BaseResult = Owned(Base); - Result = LookupMemberExpr(R, BaseResult, IsArrow, OpLoc, - SS, ObjCImpDecl, TemplateArgs != 0); - if (BaseResult.isInvalid()) - return ExprError(); - Base = BaseResult.take(); - - if (Result.isInvalid()) { - Owned(Base); - return ExprError(); - } - - if (Result.get()) { - // The only way a reference to a destructor can be used is to - // immediately call it, which falls into this case. If the - // next token is not a '(', produce a diagnostic and build the - // call now. - if (!HasTrailingLParen && - Id.getKind() == UnqualifiedId::IK_DestructorName) - return DiagnoseDtorReference(NameInfo.getLoc(), Result.get()); - - return move(Result); - } - - Result = BuildMemberReferenceExpr(Base, Base->getType(), - OpLoc, IsArrow, SS, FirstQualifierInScope, - R, TemplateArgs); - } - - return move(Result); -} - ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD, ParmVarDecl *Param) { @@ -4716,6 +3169,7 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, MarkDeclarationReferenced(Param->getDefaultArg()->getLocStart(), const_cast<CXXDestructorDecl*>(Temporary->getDestructor())); ExprTemporaries.push_back(Temporary); + ExprNeedsCleanups = true; } // We already type-checked the argument, so we know it works. @@ -4834,7 +3288,8 @@ bool Sema::GatherArgumentsForCall(SourceLocation CallLoc, InitializedEntity Entity = Param? InitializedEntity::InitializeParameter(Context, Param) - : InitializedEntity::InitializeParameter(Context, ProtoArgType); + : InitializedEntity::InitializeParameter(Context, ProtoArgType, + Proto->isArgConsumed(i)); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), Owned(Arg)); @@ -5157,7 +3612,8 @@ Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl, if (Proto && i < Proto->getNumArgs()) { InitializedEntity Entity = InitializedEntity::InitializeParameter(Context, - Proto->getArgType(i)); + Proto->getArgType(i), + Proto->isArgConsumed(i)); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), Owned(Arg)); @@ -5248,8 +3704,8 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, InitializedEntity Entity = InitializedEntity::InitializeTemporary(literalType); InitializationKind Kind - = InitializationKind::CreateCast(SourceRange(LParenLoc, RParenLoc), - /*IsCStyleCast=*/true); + = InitializationKind::CreateCStyleCast(LParenLoc, + SourceRange(LParenLoc, RParenLoc)); InitializationSequence InitSeq(*this, Entity, Kind, &literalExpr, 1); ExprResult Result = InitSeq.Perform(*this, Entity, Kind, MultiExprArg(*this, &literalExpr, 1), @@ -5267,8 +3723,9 @@ Sema::BuildCompoundLiteralExpr(SourceLocation LParenLoc, TypeSourceInfo *TInfo, // In C, compound literals are l-values for some reason. ExprValueKind VK = getLangOptions().CPlusPlus ? VK_RValue : VK_LValue; - return Owned(new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, - VK, literalExpr, isFileScope)); + return MaybeBindToTemporary( + new (Context) CompoundLiteralExpr(LParenLoc, TInfo, literalType, + VK, literalExpr, isFileScope)); } ExprResult @@ -5426,14 +3883,15 @@ static CastKind PrepareScalarCast(Sema &S, ExprResult &Src, QualType DestTy) { } /// CheckCastTypes - Check type constraints for casting between types. -ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType, - Expr *castExpr, CastKind& Kind, ExprValueKind &VK, +ExprResult Sema::CheckCastTypes(SourceLocation CastStartLoc, SourceRange TyR, + QualType castType, Expr *castExpr, + CastKind& Kind, ExprValueKind &VK, CXXCastPath &BasePath, bool FunctionalStyle) { if (castExpr->getType() == Context.UnknownAnyTy) return checkUnknownAnyCast(TyR, castType, castExpr, Kind, VK, BasePath); if (getLangOptions().CPlusPlus) - return CXXCheckCStyleCast(SourceRange(TyR.getBegin(), + return CXXCheckCStyleCast(SourceRange(CastStartLoc, castExpr->getLocEnd()), castType, VK, castExpr, Kind, BasePath, FunctionalStyle); @@ -5551,8 +4009,8 @@ ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType, // If either type is a pointer, the other type has to be either an // integer or a pointer. + QualType castExprType = castExpr->getType(); if (!castType->isArithmeticType()) { - QualType castExprType = castExpr->getType(); if (!castExprType->isIntegralType(Context) && castExprType->isArithmeticType()) { Diag(castExpr->getLocStart(), @@ -5568,6 +4026,36 @@ ExprResult Sema::CheckCastTypes(SourceRange TyR, QualType castType, } } + if (getLangOptions().ObjCAutoRefCount) { + // Diagnose problems with Objective-C casts involving lifetime qualifiers. + CheckObjCARCConversion(SourceRange(CastStartLoc, castExpr->getLocEnd()), + castType, castExpr, CCK_CStyleCast); + + if (const PointerType *CastPtr = castType->getAs<PointerType>()) { + if (const PointerType *ExprPtr = castExprType->getAs<PointerType>()) { + Qualifiers CastQuals = CastPtr->getPointeeType().getQualifiers(); + Qualifiers ExprQuals = ExprPtr->getPointeeType().getQualifiers(); + if (CastPtr->getPointeeType()->isObjCLifetimeType() && + ExprPtr->getPointeeType()->isObjCLifetimeType() && + !CastQuals.compatiblyIncludesObjCLifetime(ExprQuals)) { + Diag(castExpr->getLocStart(), + diag::err_typecheck_incompatible_ownership) + << castExprType << castType << AA_Casting + << castExpr->getSourceRange(); + + return ExprError(); + } + } + } + else if (!CheckObjCARCUnavailableWeakConversion(castType, castExprType)) { + Diag(castExpr->getLocStart(), + diag::err_arc_convesion_of_weak_unavailable) << 1 + << castExprType << castType + << castExpr->getSourceRange(); + return ExprError(); + } + } + castExprRes = Owned(castExpr); Kind = PrepareScalarCast(*this, castExprRes, castType); if (castExprRes.isInvalid()) @@ -5638,20 +4126,57 @@ ExprResult Sema::CheckExtVectorCast(SourceRange R, QualType DestTy, } ExprResult -Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, ParsedType Ty, +Sema::ActOnCastExpr(Scope *S, SourceLocation LParenLoc, + Declarator &D, ParsedType &Ty, SourceLocation RParenLoc, Expr *castExpr) { - assert((Ty != 0) && (castExpr != 0) && + assert(!D.isInvalidType() && (castExpr != 0) && "ActOnCastExpr(): missing type or expr"); - TypeSourceInfo *castTInfo; - QualType castType = GetTypeFromParser(Ty, &castTInfo); - if (!castTInfo) - castTInfo = Context.getTrivialTypeSourceInfo(castType); + TypeSourceInfo *castTInfo = GetTypeForDeclaratorCast(D, castExpr->getType()); + if (D.isInvalidType()) + return ExprError(); + + if (getLangOptions().CPlusPlus) { + // Check that there are no default arguments (C++ only). + CheckExtraCXXDefaultArguments(D); + } + + QualType castType = castTInfo->getType(); + Ty = CreateParsedType(castType, castTInfo); + + bool isVectorLiteral = false; + + // Check for an altivec or OpenCL literal, + // i.e. all the elements are integer constants. + ParenExpr *PE = dyn_cast<ParenExpr>(castExpr); + ParenListExpr *PLE = dyn_cast<ParenListExpr>(castExpr); + if (getLangOptions().AltiVec && castType->isVectorType() && (PE || PLE)) { + if (PLE && PLE->getNumExprs() == 0) { + Diag(PLE->getExprLoc(), diag::err_altivec_empty_initializer); + return ExprError(); + } + if (PE || PLE->getNumExprs() == 1) { + Expr *E = (PE ? PE->getSubExpr() : PLE->getExpr(0)); + if (!E->getType()->isVectorType()) + isVectorLiteral = true; + } + else + isVectorLiteral = true; + } + + // If this is a vector initializer, '(' type ')' '(' init, ..., init ')' + // then handle it as such. + if (isVectorLiteral) + return BuildVectorLiteral(LParenLoc, RParenLoc, castExpr, castTInfo); // If the Expr being casted is a ParenListExpr, handle it specially. - if (isa<ParenListExpr>(castExpr)) - return ActOnCastOfParenListExpr(S, LParenLoc, RParenLoc, castExpr, - castTInfo); + // This is not an AltiVec-style cast, so turn the ParenListExpr into a + // sequence of BinOp comma operators. + if (isa<ParenListExpr>(castExpr)) { + ExprResult Result = MaybeConvertParenListExprToParenExpr(S, castExpr); + if (Result.isInvalid()) return ExprError(); + castExpr = Result.take(); + } return BuildCStyleCastExpr(LParenLoc, castTInfo, RParenLoc, castExpr); } @@ -5663,8 +4188,8 @@ Sema::BuildCStyleCastExpr(SourceLocation LParenLoc, TypeSourceInfo *Ty, ExprValueKind VK = VK_RValue; CXXCastPath BasePath; ExprResult CastResult = - CheckCastTypes(SourceRange(LParenLoc, RParenLoc), Ty->getType(), castExpr, - Kind, VK, BasePath); + CheckCastTypes(LParenLoc, SourceRange(LParenLoc, RParenLoc), Ty->getType(), + castExpr, Kind, VK, BasePath); if (CastResult.isInvalid()) return ExprError(); castExpr = CastResult.take(); @@ -5675,6 +4200,80 @@ Sema::BuildCStyleCastExpr(SourceLocation LParenLoc, TypeSourceInfo *Ty, LParenLoc, RParenLoc)); } +ExprResult Sema::BuildVectorLiteral(SourceLocation LParenLoc, + SourceLocation RParenLoc, Expr *E, + TypeSourceInfo *TInfo) { + assert((isa<ParenListExpr>(E) || isa<ParenExpr>(E)) && + "Expected paren or paren list expression"); + + Expr **exprs; + unsigned numExprs; + Expr *subExpr; + if (ParenListExpr *PE = dyn_cast<ParenListExpr>(E)) { + exprs = PE->getExprs(); + numExprs = PE->getNumExprs(); + } else { + subExpr = cast<ParenExpr>(E)->getSubExpr(); + exprs = &subExpr; + numExprs = 1; + } + + QualType Ty = TInfo->getType(); + assert(Ty->isVectorType() && "Expected vector type"); + + llvm::SmallVector<Expr *, 8> initExprs; + const VectorType *VTy = Ty->getAs<VectorType>(); + unsigned numElems = Ty->getAs<VectorType>()->getNumElements(); + + // '(...)' form of vector initialization in AltiVec: the number of + // initializers must be one or must match the size of the vector. + // If a single value is specified in the initializer then it will be + // replicated to all the components of the vector + if (VTy->getVectorKind() == VectorType::AltiVecVector) { + // The number of initializers must be one or must match the size of the + // vector. If a single value is specified in the initializer then it will + // be replicated to all the components of the vector + if (numExprs == 1) { + QualType ElemTy = Ty->getAs<VectorType>()->getElementType(); + ExprResult Literal = Owned(exprs[0]); + Literal = ImpCastExprToType(Literal.take(), ElemTy, + PrepareScalarCast(*this, Literal, ElemTy)); + return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.take()); + } + else if (numExprs < numElems) { + Diag(E->getExprLoc(), + diag::err_incorrect_number_of_vector_initializers); + return ExprError(); + } + else + for (unsigned i = 0, e = numExprs; i != e; ++i) + initExprs.push_back(exprs[i]); + } + else { + // For OpenCL, when the number of initializers is a single value, + // it will be replicated to all components of the vector. + if (getLangOptions().OpenCL && + VTy->getVectorKind() == VectorType::GenericVector && + numExprs == 1) { + QualType ElemTy = Ty->getAs<VectorType>()->getElementType(); + ExprResult Literal = Owned(exprs[0]); + Literal = ImpCastExprToType(Literal.take(), ElemTy, + PrepareScalarCast(*this, Literal, ElemTy)); + return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.take()); + } + + for (unsigned i = 0, e = numExprs; i != e; ++i) + initExprs.push_back(exprs[i]); + } + // FIXME: This means that pretty-printing the final AST will produce curly + // braces instead of the original commas. + InitListExpr *initE = new (Context) InitListExpr(Context, LParenLoc, + &initExprs[0], + initExprs.size(), RParenLoc); + initE->setType(Ty); + return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, initE); +} + /// This is not an AltiVec-style cast, so turn the ParenListExpr into a sequence /// of comma binary operators. ExprResult @@ -5694,91 +4293,18 @@ Sema::MaybeConvertParenListExprToParenExpr(Scope *S, Expr *expr) { return ActOnParenExpr(E->getLParenLoc(), E->getRParenLoc(), Result.get()); } -ExprResult -Sema::ActOnCastOfParenListExpr(Scope *S, SourceLocation LParenLoc, - SourceLocation RParenLoc, Expr *Op, - TypeSourceInfo *TInfo) { - ParenListExpr *PE = cast<ParenListExpr>(Op); - QualType Ty = TInfo->getType(); - bool isVectorLiteral = false; - - // Check for an altivec or OpenCL literal, - // i.e. all the elements are integer constants. - if (getLangOptions().AltiVec && Ty->isVectorType()) { - if (PE->getNumExprs() == 0) { - Diag(PE->getExprLoc(), diag::err_altivec_empty_initializer); - return ExprError(); - } - if (PE->getNumExprs() == 1) { - if (!PE->getExpr(0)->getType()->isVectorType()) - isVectorLiteral = true; - } - else - isVectorLiteral = true; - } - - // If this is a vector initializer, '(' type ')' '(' init, ..., init ')' - // then handle it as such. - if (isVectorLiteral) { - llvm::SmallVector<Expr *, 8> initExprs; - // '(...)' form of vector initialization in AltiVec: the number of - // initializers must be one or must match the size of the vector. - // If a single value is specified in the initializer then it will be - // replicated to all the components of the vector - if (Ty->getAs<VectorType>()->getVectorKind() == - VectorType::AltiVecVector) { - unsigned numElems = Ty->getAs<VectorType>()->getNumElements(); - // The number of initializers must be one or must match the size of the - // vector. If a single value is specified in the initializer then it will - // be replicated to all the components of the vector - if (PE->getNumExprs() == 1) { - QualType ElemTy = Ty->getAs<VectorType>()->getElementType(); - ExprResult Literal = Owned(PE->getExpr(0)); - Literal = ImpCastExprToType(Literal.take(), ElemTy, - PrepareScalarCast(*this, Literal, ElemTy)); - return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Literal.take()); - } - else if (PE->getNumExprs() < numElems) { - Diag(PE->getExprLoc(), - diag::err_incorrect_number_of_vector_initializers); - return ExprError(); - } - else - for (unsigned i = 0, e = PE->getNumExprs(); i != e; ++i) - initExprs.push_back(PE->getExpr(i)); - } - else - for (unsigned i = 0, e = PE->getNumExprs(); i != e; ++i) - initExprs.push_back(PE->getExpr(i)); - - // FIXME: This means that pretty-printing the final AST will produce curly - // braces instead of the original commas. - InitListExpr *E = new (Context) InitListExpr(Context, LParenLoc, - &initExprs[0], - initExprs.size(), RParenLoc); - E->setType(Ty); - return BuildCompoundLiteralExpr(LParenLoc, TInfo, RParenLoc, E); - } else { - // This is not an AltiVec-style cast, so turn the ParenListExpr into a - // sequence of BinOp comma operators. - ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Op); - if (Result.isInvalid()) return ExprError(); - return BuildCStyleCastExpr(LParenLoc, TInfo, RParenLoc, Result.take()); - } -} - ExprResult Sema::ActOnParenOrParenListExpr(SourceLocation L, SourceLocation R, - MultiExprArg Val, - ParsedType TypeOfCast) { + MultiExprArg Val) { unsigned nexprs = Val.size(); Expr **exprs = reinterpret_cast<Expr**>(Val.release()); assert((exprs != 0) && "ActOnParenOrParenListExpr() missing expr list"); Expr *expr; - if (nexprs == 1 && TypeOfCast && !TypeIsVectorType(TypeOfCast)) + if (nexprs == 1) expr = new (Context) ParenExpr(L, R, exprs[0]); else - expr = new (Context) ParenListExpr(Context, L, exprs, nexprs, R); + expr = new (Context) ParenListExpr(Context, L, exprs, nexprs, R, + exprs[nexprs-1]->getType()); return Owned(expr); } @@ -5876,7 +4402,7 @@ QualType Sema::CheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, ExprR // Now check the two expressions. if (LHSTy->isVectorType() || RHSTy->isVectorType()) - return CheckVectorOperands(QuestionLoc, LHS, RHS); + return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/false); // OpenCL: If the condition is a vector, and both operands are scalar, // attempt to implicity convert them to the vector type to act like the @@ -6203,43 +4729,21 @@ QualType Sema::FindCompositeObjCPointerType(ExprResult &LHS, ExprResult &RHS, return QualType(); } -/// SuggestParentheses - Emit a diagnostic together with a fixit hint that wraps +/// SuggestParentheses - Emit a note with a fixit hint that wraps /// ParenRange in parentheses. static void SuggestParentheses(Sema &Self, SourceLocation Loc, - const PartialDiagnostic &PD, - const PartialDiagnostic &FirstNote, - SourceRange FirstParenRange, - const PartialDiagnostic &SecondNote, - SourceRange SecondParenRange) { - Self.Diag(Loc, PD); - - if (!FirstNote.getDiagID()) - return; - - SourceLocation EndLoc = Self.PP.getLocForEndOfToken(FirstParenRange.getEnd()); - if (!FirstParenRange.getEnd().isFileID() || EndLoc.isInvalid()) { - // We can't display the parentheses, so just return. - return; - } - - Self.Diag(Loc, FirstNote) - << FixItHint::CreateInsertion(FirstParenRange.getBegin(), "(") - << FixItHint::CreateInsertion(EndLoc, ")"); - - if (!SecondNote.getDiagID()) - return; - - EndLoc = Self.PP.getLocForEndOfToken(SecondParenRange.getEnd()); - if (!SecondParenRange.getEnd().isFileID() || EndLoc.isInvalid()) { - // We can't display the parentheses, so just dig the - // warning/error and return. - Self.Diag(Loc, SecondNote); - return; + const PartialDiagnostic &Note, + SourceRange ParenRange) { + SourceLocation EndLoc = Self.PP.getLocForEndOfToken(ParenRange.getEnd()); + if (ParenRange.getBegin().isFileID() && ParenRange.getEnd().isFileID() && + EndLoc.isValid()) { + Self.Diag(Loc, Note) + << FixItHint::CreateInsertion(ParenRange.getBegin(), "(") + << FixItHint::CreateInsertion(EndLoc, ")"); + } else { + // We can't display the parentheses, so just show the bare note. + Self.Diag(Loc, Note) << ParenRange; } - - Self.Diag(Loc, SecondNote) - << FixItHint::CreateInsertion(SecondParenRange.getBegin(), "(") - << FixItHint::CreateInsertion(EndLoc, ")"); } static bool IsArithmeticOp(BinaryOperatorKind Opc) { @@ -6312,13 +4816,13 @@ static bool ExprLooksBoolean(Expr *E) { /// "int x = a + someBinaryCondition ? 1 : 2". static void DiagnoseConditionalPrecedence(Sema &Self, SourceLocation OpLoc, - Expr *cond, - Expr *lhs, - Expr *rhs) { + Expr *Condition, + Expr *LHS, + Expr *RHS) { BinaryOperatorKind CondOpcode; Expr *CondRHS; - if (!IsArithmeticBinaryExpr(cond, &CondOpcode, &CondRHS)) + if (!IsArithmeticBinaryExpr(Condition, &CondOpcode, &CondRHS)) return; if (!ExprLooksBoolean(CondRHS)) return; @@ -6326,25 +4830,18 @@ static void DiagnoseConditionalPrecedence(Sema &Self, // The condition is an arithmetic binary expression, with a right- // hand side that looks boolean, so warn. - PartialDiagnostic Warn = Self.PDiag(diag::warn_precedence_conditional) - << cond->getSourceRange() - << BinaryOperator::getOpcodeStr(CondOpcode); - - PartialDiagnostic FirstNote = - Self.PDiag(diag::note_precedence_conditional_silence) + Self.Diag(OpLoc, diag::warn_precedence_conditional) + << Condition->getSourceRange() << BinaryOperator::getOpcodeStr(CondOpcode); - SourceRange FirstParenRange(cond->getLocStart(), - cond->getLocEnd()); - - PartialDiagnostic SecondNote = - Self.PDiag(diag::note_precedence_conditional_first); - - SourceRange SecondParenRange(CondRHS->getLocStart(), - rhs->getLocEnd()); + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_conditional_silence) + << BinaryOperator::getOpcodeStr(CondOpcode), + SourceRange(Condition->getLocStart(), Condition->getLocEnd())); - SuggestParentheses(Self, OpLoc, Warn, FirstNote, FirstParenRange, - SecondNote, SecondParenRange); + SuggestParentheses(Self, OpLoc, + Self.PDiag(diag::note_precedence_conditional_first), + SourceRange(CondRHS->getLocStart(), RHS->getLocEnd())); } /// ActOnConditionalOp - Parse a ?: operation. Note that 'LHS' may be null @@ -6427,17 +4924,31 @@ checkPointerTypesForAssignment(Sema &S, QualType lhsType, QualType rhsType) { // qualifiers of the type *pointed to* by the right; Qualifiers lq; + // As a special case, 'non-__weak A *' -> 'non-__weak const *' is okay. + if (lhq.getObjCLifetime() != rhq.getObjCLifetime() && + lhq.compatiblyIncludesObjCLifetime(rhq)) { + // Ignore lifetime for further calculation. + lhq.removeObjCLifetime(); + rhq.removeObjCLifetime(); + } + if (!lhq.compatiblyIncludes(rhq)) { // Treat address-space mismatches as fatal. TODO: address subspaces if (lhq.getAddressSpace() != rhq.getAddressSpace()) ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; - // It's okay to add or remove GC qualifiers when converting to + // It's okay to add or remove GC or lifetime qualifiers when converting to // and from void*. - else if (lhq.withoutObjCGCAttr().compatiblyIncludes(rhq.withoutObjCGCAttr()) + else if (lhq.withoutObjCGCAttr().withoutObjCGLifetime() + .compatiblyIncludes( + rhq.withoutObjCGCAttr().withoutObjCGLifetime()) && (lhptee->isVoidType() || rhptee->isVoidType())) ; // keep old + // Treat lifetime mismatches as fatal. + else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) + ConvTy = Sema::IncompatiblePointerDiscardsQualifiers; + // For GCC compatibility, other qualifier mismatches are treated // as still compatible in C. else ConvTy = Sema::CompatiblePointerDiscardsQualifiers; @@ -6614,6 +5125,7 @@ Sema::AssignConvertType Sema::CheckAssignmentConstraints(QualType lhsType, ExprResult &rhs, CastKind &Kind) { QualType rhsType = rhs.get()->getType(); + QualType origLhsType = lhsType; // Get canonical types. We're not formatting these types, just comparing // them. @@ -6768,7 +5280,13 @@ Sema::CheckAssignmentConstraints(QualType lhsType, ExprResult &rhs, // A* -> B* if (rhsType->isObjCObjectPointerType()) { Kind = CK_BitCast; - return checkObjCPointerTypesForAssignment(*this, lhsType, rhsType); + Sema::AssignConvertType result = + checkObjCPointerTypesForAssignment(*this, lhsType, rhsType); + if (getLangOptions().ObjCAutoRefCount && + result == Compatible && + !CheckObjCARCUnavailableWeakConversion(origLhsType, rhsType)) + result = IncompatibleObjCWeakRef; + return result; } // int or null -> A* @@ -6936,8 +5454,12 @@ Sema::CheckSingleAssignmentConstraints(QualType lhsType, ExprResult &rExpr) { AA_Assigning); if (Res.isInvalid()) return Incompatible; + Sema::AssignConvertType result = Compatible; + if (getLangOptions().ObjCAutoRefCount && + !CheckObjCARCUnavailableWeakConversion(lhsType, rExpr.get()->getType())) + result = IncompatibleObjCWeakRef; rExpr = move(Res); - return Compatible; + return result; } // FIXME: Currently, we fall through and treat C++ classes like C @@ -6989,7 +5511,8 @@ QualType Sema::InvalidOperands(SourceLocation Loc, ExprResult &lex, ExprResult & return QualType(); } -QualType Sema::CheckVectorOperands(SourceLocation Loc, ExprResult &lex, ExprResult &rex) { +QualType Sema::CheckVectorOperands(ExprResult &lex, ExprResult &rex, + SourceLocation Loc, bool isCompAssign) { // For conversion purposes, we ignore any qualifiers. // For example, "const float" and "float" are equivalent. QualType lhsType = @@ -7001,42 +5524,33 @@ QualType Sema::CheckVectorOperands(SourceLocation Loc, ExprResult &lex, ExprResu if (lhsType == rhsType) return lhsType; - // Handle the case of a vector & extvector type of the same size and element - // type. It would be nice if we only had one vector type someday. - if (getLangOptions().LaxVectorConversions) { - if (const VectorType *LV = lhsType->getAs<VectorType>()) { - if (const VectorType *RV = rhsType->getAs<VectorType>()) { - if (LV->getElementType() == RV->getElementType() && - LV->getNumElements() == RV->getNumElements()) { - if (lhsType->isExtVectorType()) { - rex = ImpCastExprToType(rex.take(), lhsType, CK_BitCast); - return lhsType; - } - - lex = ImpCastExprToType(lex.take(), rhsType, CK_BitCast); - return rhsType; - } else if (Context.getTypeSize(lhsType) ==Context.getTypeSize(rhsType)){ - // If we are allowing lax vector conversions, and LHS and RHS are both - // vectors, the total size only needs to be the same. This is a - // bitcast; no bits are changed but the result type is different. - rex = ImpCastExprToType(rex.take(), lhsType, CK_BitCast); - return lhsType; - } - } - } - } - // Handle the case of equivalent AltiVec and GCC vector types if (lhsType->isVectorType() && rhsType->isVectorType() && Context.areCompatibleVectorTypes(lhsType, rhsType)) { - lex = ImpCastExprToType(lex.take(), rhsType, CK_BitCast); + if (lhsType->isExtVectorType()) { + rex = ImpCastExprToType(rex.take(), lhsType, CK_BitCast); + return lhsType; + } + + if (!isCompAssign) + lex = ImpCastExprToType(lex.take(), rhsType, CK_BitCast); return rhsType; } + if (getLangOptions().LaxVectorConversions && + Context.getTypeSize(lhsType) == Context.getTypeSize(rhsType)) { + // If we are allowing lax vector conversions, and LHS and RHS are both + // vectors, the total size only needs to be the same. This is a + // bitcast; no bits are changed but the result type is different. + // FIXME: Should we really be allowing this? + rex = ImpCastExprToType(rex.take(), lhsType, CK_BitCast); + return lhsType; + } + // Canonicalize the ExtVector to the LHS, remember if we swapped so we can // swap back (so that we don't reverse the inputs to a subtract, for instance. bool swapped = false; - if (rhsType->isExtVectorType()) { + if (rhsType->isExtVectorType() && !isCompAssign) { swapped = true; std::swap(rex, lex); std::swap(rhsType, lhsType); @@ -7069,6 +5583,7 @@ QualType Sema::CheckVectorOperands(SourceLocation Loc, ExprResult &lex, ExprResu } // Vectors of different size or scalar and non-ext-vector are errors. + if (swapped) std::swap(rex, lex); Diag(Loc, diag::err_typecheck_vector_not_convertable) << lex.get()->getType() << rex.get()->getType() << lex.get()->getSourceRange() << rex.get()->getSourceRange(); @@ -7078,7 +5593,7 @@ QualType Sema::CheckVectorOperands(SourceLocation Loc, ExprResult &lex, ExprResu QualType Sema::CheckMultiplyDivideOperands( ExprResult &lex, ExprResult &rex, SourceLocation Loc, bool isCompAssign, bool isDiv) { if (lex.get()->getType()->isVectorType() || rex.get()->getType()->isVectorType()) - return CheckVectorOperands(Loc, lex, rex); + return CheckVectorOperands(lex, rex, Loc, isCompAssign); QualType compType = UsualArithmeticConversions(lex, rex, isCompAssign); if (lex.isInvalid() || rex.isInvalid()) @@ -7102,7 +5617,7 @@ QualType Sema::CheckRemainderOperands( if (lex.get()->getType()->isVectorType() || rex.get()->getType()->isVectorType()) { if (lex.get()->getType()->hasIntegerRepresentation() && rex.get()->getType()->hasIntegerRepresentation()) - return CheckVectorOperands(Loc, lex, rex); + return CheckVectorOperands(lex, rex, Loc, isCompAssign); return InvalidOperands(Loc, lex, rex); } @@ -7121,10 +5636,149 @@ QualType Sema::CheckRemainderOperands( return compType; } +/// \brief Diagnose invalid arithmetic on two void pointers. +static void diagnoseArithmeticOnTwoVoidPointers(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS) { + S.Diag(Loc, S.getLangOptions().CPlusPlus + ? diag::err_typecheck_pointer_arith_void_type + : diag::ext_gnu_void_ptr) + << 1 /* two pointers */ << LHS->getSourceRange() << RHS->getSourceRange(); +} + +/// \brief Diagnose invalid arithmetic on a void pointer. +static void diagnoseArithmeticOnVoidPointer(Sema &S, SourceLocation Loc, + Expr *Pointer) { + S.Diag(Loc, S.getLangOptions().CPlusPlus + ? diag::err_typecheck_pointer_arith_void_type + : diag::ext_gnu_void_ptr) + << 0 /* one pointer */ << Pointer->getSourceRange(); +} + +/// \brief Diagnose invalid arithmetic on two function pointers. +static void diagnoseArithmeticOnTwoFunctionPointers(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS) { + assert(LHS->getType()->isAnyPointerType()); + assert(RHS->getType()->isAnyPointerType()); + S.Diag(Loc, S.getLangOptions().CPlusPlus + ? diag::err_typecheck_pointer_arith_function_type + : diag::ext_gnu_ptr_func_arith) + << 1 /* two pointers */ << LHS->getType()->getPointeeType() + // We only show the second type if it differs from the first. + << (unsigned)!S.Context.hasSameUnqualifiedType(LHS->getType(), + RHS->getType()) + << RHS->getType()->getPointeeType() + << LHS->getSourceRange() << RHS->getSourceRange(); +} + +/// \brief Diagnose invalid arithmetic on a function pointer. +static void diagnoseArithmeticOnFunctionPointer(Sema &S, SourceLocation Loc, + Expr *Pointer) { + assert(Pointer->getType()->isAnyPointerType()); + S.Diag(Loc, S.getLangOptions().CPlusPlus + ? diag::err_typecheck_pointer_arith_function_type + : diag::ext_gnu_ptr_func_arith) + << 0 /* one pointer */ << Pointer->getType()->getPointeeType() + << 0 /* one pointer, so only one type */ + << Pointer->getSourceRange(); +} + +/// \brief Check the validity of an arithmetic pointer operand. +/// +/// If the operand has pointer type, this code will check for pointer types +/// which are invalid in arithmetic operations. These will be diagnosed +/// appropriately, including whether or not the use is supported as an +/// extension. +/// +/// \returns True when the operand is valid to use (even if as an extension). +static bool checkArithmeticOpPointerOperand(Sema &S, SourceLocation Loc, + Expr *Operand) { + if (!Operand->getType()->isAnyPointerType()) return true; + + QualType PointeeTy = Operand->getType()->getPointeeType(); + if (PointeeTy->isVoidType()) { + diagnoseArithmeticOnVoidPointer(S, Loc, Operand); + return !S.getLangOptions().CPlusPlus; + } + if (PointeeTy->isFunctionType()) { + diagnoseArithmeticOnFunctionPointer(S, Loc, Operand); + return !S.getLangOptions().CPlusPlus; + } + + if ((Operand->getType()->isPointerType() && + !Operand->getType()->isDependentType()) || + Operand->getType()->isObjCObjectPointerType()) { + QualType PointeeTy = Operand->getType()->getPointeeType(); + if (S.RequireCompleteType( + Loc, PointeeTy, + S.PDiag(diag::err_typecheck_arithmetic_incomplete_type) + << PointeeTy << Operand->getSourceRange())) + return false; + } + + return true; +} + +/// \brief Check the validity of a binary arithmetic operation w.r.t. pointer +/// operands. +/// +/// This routine will diagnose any invalid arithmetic on pointer operands much +/// like \see checkArithmeticOpPointerOperand. However, it has special logic +/// for emitting a single diagnostic even for operations where both LHS and RHS +/// are (potentially problematic) pointers. +/// +/// \returns True when the operand is valid to use (even if as an extension). +static bool checkArithmeticBinOpPointerOperands(Sema &S, SourceLocation Loc, + Expr *LHS, Expr *RHS) { + bool isLHSPointer = LHS->getType()->isAnyPointerType(); + bool isRHSPointer = RHS->getType()->isAnyPointerType(); + if (!isLHSPointer && !isRHSPointer) return true; + + QualType LHSPointeeTy, RHSPointeeTy; + if (isLHSPointer) LHSPointeeTy = LHS->getType()->getPointeeType(); + if (isRHSPointer) RHSPointeeTy = RHS->getType()->getPointeeType(); + + // Check for arithmetic on pointers to incomplete types. + bool isLHSVoidPtr = isLHSPointer && LHSPointeeTy->isVoidType(); + bool isRHSVoidPtr = isRHSPointer && RHSPointeeTy->isVoidType(); + if (isLHSVoidPtr || isRHSVoidPtr) { + if (!isRHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, LHS); + else if (!isLHSVoidPtr) diagnoseArithmeticOnVoidPointer(S, Loc, RHS); + else diagnoseArithmeticOnTwoVoidPointers(S, Loc, LHS, RHS); + + return !S.getLangOptions().CPlusPlus; + } + + bool isLHSFuncPtr = isLHSPointer && LHSPointeeTy->isFunctionType(); + bool isRHSFuncPtr = isRHSPointer && RHSPointeeTy->isFunctionType(); + if (isLHSFuncPtr || isRHSFuncPtr) { + if (!isRHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, LHS); + else if (!isLHSFuncPtr) diagnoseArithmeticOnFunctionPointer(S, Loc, RHS); + else diagnoseArithmeticOnTwoFunctionPointers(S, Loc, LHS, RHS); + + return !S.getLangOptions().CPlusPlus; + } + + Expr *Operands[] = { LHS, RHS }; + for (unsigned i = 0; i < 2; ++i) { + Expr *Operand = Operands[i]; + if ((Operand->getType()->isPointerType() && + !Operand->getType()->isDependentType()) || + Operand->getType()->isObjCObjectPointerType()) { + QualType PointeeTy = Operand->getType()->getPointeeType(); + if (S.RequireCompleteType( + Loc, PointeeTy, + S.PDiag(diag::err_typecheck_arithmetic_incomplete_type) + << PointeeTy << Operand->getSourceRange())) + return false; + } + } + return true; +} + QualType Sema::CheckAdditionOperands( // C99 6.5.6 ExprResult &lex, ExprResult &rex, SourceLocation Loc, QualType* CompLHSTy) { if (lex.get()->getType()->isVectorType() || rex.get()->getType()->isVectorType()) { - QualType compType = CheckVectorOperands(Loc, lex, rex); + QualType compType = CheckVectorOperands(lex, rex, Loc, CompLHSTy); if (CompLHSTy) *CompLHSTy = compType; return compType; } @@ -7146,42 +5800,12 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6 std::swap(PExp, IExp); if (PExp->getType()->isAnyPointerType()) { - if (IExp->getType()->isIntegerType()) { - QualType PointeeTy = PExp->getType()->getPointeeType(); - - // Check for arithmetic on pointers to incomplete types. - if (PointeeTy->isVoidType()) { - if (getLangOptions().CPlusPlus) { - Diag(Loc, diag::err_typecheck_pointer_arith_void_type) - << lex.get()->getSourceRange() << rex.get()->getSourceRange(); - return QualType(); - } + if (!checkArithmeticOpPointerOperand(*this, Loc, PExp)) + return QualType(); - // GNU extension: arithmetic on pointer to void - Diag(Loc, diag::ext_gnu_void_ptr) - << lex.get()->getSourceRange() << rex.get()->getSourceRange(); - } else if (PointeeTy->isFunctionType()) { - if (getLangOptions().CPlusPlus) { - Diag(Loc, diag::err_typecheck_pointer_arith_function_type) - << lex.get()->getType() << lex.get()->getSourceRange(); - return QualType(); - } + QualType PointeeTy = PExp->getType()->getPointeeType(); - // GNU extension: arithmetic on pointer to function - Diag(Loc, diag::ext_gnu_ptr_func_arith) - << lex.get()->getType() << lex.get()->getSourceRange(); - } else { - // Check if we require a complete type. - if (((PExp->getType()->isPointerType() && - !PExp->getType()->isDependentType()) || - PExp->getType()->isObjCObjectPointerType()) && - RequireCompleteType(Loc, PointeeTy, - PDiag(diag::err_typecheck_arithmetic_incomplete_type) - << PExp->getSourceRange() - << PExp->getType())) - return QualType(); - } // Diagnose bad cases where we step over interface counts. if (PointeeTy->isObjCObjectType() && LangOpts.ObjCNonFragileABI) { Diag(Loc, diag::err_arithmetic_nonfragile_interface) @@ -7209,7 +5833,7 @@ QualType Sema::CheckAdditionOperands( // C99 6.5.6 QualType Sema::CheckSubtractionOperands(ExprResult &lex, ExprResult &rex, SourceLocation Loc, QualType* CompLHSTy) { if (lex.get()->getType()->isVectorType() || rex.get()->getType()->isVectorType()) { - QualType compType = CheckVectorOperands(Loc, lex, rex); + QualType compType = CheckVectorOperands(lex, rex, Loc, CompLHSTy); if (CompLHSTy) *CompLHSTy = compType; return compType; } @@ -7231,35 +5855,6 @@ QualType Sema::CheckSubtractionOperands(ExprResult &lex, ExprResult &rex, if (lex.get()->getType()->isAnyPointerType()) { QualType lpointee = lex.get()->getType()->getPointeeType(); - // The LHS must be an completely-defined object type. - - bool ComplainAboutVoid = false; - Expr *ComplainAboutFunc = 0; - if (lpointee->isVoidType()) { - if (getLangOptions().CPlusPlus) { - Diag(Loc, diag::err_typecheck_pointer_arith_void_type) - << lex.get()->getSourceRange() << rex.get()->getSourceRange(); - return QualType(); - } - - // GNU C extension: arithmetic on pointer to void - ComplainAboutVoid = true; - } else if (lpointee->isFunctionType()) { - if (getLangOptions().CPlusPlus) { - Diag(Loc, diag::err_typecheck_pointer_arith_function_type) - << lex.get()->getType() << lex.get()->getSourceRange(); - return QualType(); - } - - // GNU C extension: arithmetic on pointer to function - ComplainAboutFunc = lex.get(); - } else if (!lpointee->isDependentType() && - RequireCompleteType(Loc, lpointee, - PDiag(diag::err_typecheck_sub_ptr_object) - << lex.get()->getSourceRange() - << lex.get()->getType())) - return QualType(); - // Diagnose bad cases where we step over interface counts. if (lpointee->isObjCObjectType() && LangOpts.ObjCNonFragileABI) { Diag(Loc, diag::err_arithmetic_nonfragile_interface) @@ -7269,13 +5864,8 @@ QualType Sema::CheckSubtractionOperands(ExprResult &lex, ExprResult &rex, // The result type of a pointer-int computation is the pointer type. if (rex.get()->getType()->isIntegerType()) { - if (ComplainAboutVoid) - Diag(Loc, diag::ext_gnu_void_ptr) - << lex.get()->getSourceRange() << rex.get()->getSourceRange(); - if (ComplainAboutFunc) - Diag(Loc, diag::ext_gnu_ptr_func_arith) - << ComplainAboutFunc->getType() - << ComplainAboutFunc->getSourceRange(); + if (!checkArithmeticOpPointerOperand(*this, Loc, lex.get())) + return QualType(); if (CompLHSTy) *CompLHSTy = lex.get()->getType(); return lex.get()->getType(); @@ -7285,33 +5875,6 @@ QualType Sema::CheckSubtractionOperands(ExprResult &lex, ExprResult &rex, if (const PointerType *RHSPTy = rex.get()->getType()->getAs<PointerType>()) { QualType rpointee = RHSPTy->getPointeeType(); - // RHS must be a completely-type object type. - // Handle the GNU void* extension. - if (rpointee->isVoidType()) { - if (getLangOptions().CPlusPlus) { - Diag(Loc, diag::err_typecheck_pointer_arith_void_type) - << lex.get()->getSourceRange() << rex.get()->getSourceRange(); - return QualType(); - } - - ComplainAboutVoid = true; - } else if (rpointee->isFunctionType()) { - if (getLangOptions().CPlusPlus) { - Diag(Loc, diag::err_typecheck_pointer_arith_function_type) - << rex.get()->getType() << rex.get()->getSourceRange(); - return QualType(); - } - - // GNU extension: arithmetic on pointer to function - if (!ComplainAboutFunc) - ComplainAboutFunc = rex.get(); - } else if (!rpointee->isDependentType() && - RequireCompleteType(Loc, rpointee, - PDiag(diag::err_typecheck_sub_ptr_object) - << rex.get()->getSourceRange() - << rex.get()->getType())) - return QualType(); - if (getLangOptions().CPlusPlus) { // Pointee types must be the same: C++ [expr.add] if (!Context.hasSameUnqualifiedType(lpointee, rpointee)) { @@ -7332,13 +5895,9 @@ QualType Sema::CheckSubtractionOperands(ExprResult &lex, ExprResult &rex, } } - if (ComplainAboutVoid) - Diag(Loc, diag::ext_gnu_void_ptr) - << lex.get()->getSourceRange() << rex.get()->getSourceRange(); - if (ComplainAboutFunc) - Diag(Loc, diag::ext_gnu_ptr_func_arith) - << ComplainAboutFunc->getType() - << ComplainAboutFunc->getSourceRange(); + if (!checkArithmeticBinOpPointerOperands(*this, Loc, + lex.get(), rex.get())) + return QualType(); if (CompLHSTy) *CompLHSTy = lex.get()->getType(); return Context.getPointerDiffType(); @@ -7394,19 +5953,24 @@ static void DiagnoseBadShiftValues(Sema& S, ExprResult &lex, ExprResult &rex, llvm::APSInt Result = Left.extend(ResultBits.getLimitedValue()); Result = Result.shl(Right); + // Print the bit representation of the signed integer as an unsigned + // hexadecimal number. + llvm::SmallString<40> HexResult; + Result.toString(HexResult, 16, /*Signed =*/false, /*Literal =*/true); + // If we are only missing a sign bit, this is less likely to result in actual // bugs -- if the result is cast back to an unsigned type, it will have the // expected value. Thus we place this behind a different warning that can be // turned off separately if needed. if (LeftBits == ResultBits - 1) { - S.Diag(Loc, diag::warn_shift_result_overrides_sign_bit) - << Result.toString(10) << LHSTy + S.Diag(Loc, diag::warn_shift_result_sets_sign_bit) + << HexResult.str() << LHSTy << lex.get()->getSourceRange() << rex.get()->getSourceRange(); return; } S.Diag(Loc, diag::warn_shift_result_gt_typewidth) - << Result.toString(10) << Result.getMinSignedBits() << LHSTy + << HexResult.str() << Result.getMinSignedBits() << LHSTy << Left.getBitWidth() << lex.get()->getSourceRange() << rex.get()->getSourceRange(); } @@ -7427,7 +5991,7 @@ QualType Sema::CheckShiftOperands(ExprResult &lex, ExprResult &rex, SourceLocati // Vector shifts promote their scalar inputs to vector type. if (lex.get()->getType()->isVectorType() || rex.get()->getType()->isVectorType()) - return CheckVectorOperands(Loc, lex, rex); + return CheckVectorOperands(lex, rex, Loc, isCompAssign); // Shifts don't perform usual arithmetic conversions, they just do integer // promotions on each operand. C99 6.5.7p3 @@ -7720,7 +6284,8 @@ QualType Sema::CheckCompareOperands(ExprResult &lex, ExprResult &rex, SourceLoca // comparisons of member pointers to null pointer constants. if (RHSIsNull && ((lType->isAnyPointerType() || lType->isNullPtrType()) || - (!isRelational && lType->isMemberPointerType()))) { + (!isRelational && + (lType->isMemberPointerType() || lType->isBlockPointerType())))) { rex = ImpCastExprToType(rex.take(), lType, lType->isMemberPointerType() ? CK_NullToMemberPointer @@ -7729,7 +6294,8 @@ QualType Sema::CheckCompareOperands(ExprResult &lex, ExprResult &rex, SourceLoca } if (LHSIsNull && ((rType->isAnyPointerType() || rType->isNullPtrType()) || - (!isRelational && rType->isMemberPointerType()))) { + (!isRelational && + (rType->isMemberPointerType() || rType->isBlockPointerType())))) { lex = ImpCastExprToType(lex.take(), rType, rType->isMemberPointerType() ? CK_NullToMemberPointer @@ -7894,7 +6460,7 @@ QualType Sema::CheckVectorCompareOperands(ExprResult &lex, ExprResult &rex, bool isRelational) { // Check to make sure we're operating on vectors of the same type and width, // Allowing one side to be a scalar of element type. - QualType vType = CheckVectorOperands(Loc, lex, rex); + QualType vType = CheckVectorOperands(lex, rex, Loc, /*isCompAssign*/false); if (vType.isNull()) return vType; @@ -7949,7 +6515,7 @@ inline QualType Sema::CheckBitwiseOperands( if (lex.get()->getType()->isVectorType() || rex.get()->getType()->isVectorType()) { if (lex.get()->getType()->hasIntegerRepresentation() && rex.get()->getType()->hasIntegerRepresentation()) - return CheckVectorOperands(Loc, lex, rex); + return CheckVectorOperands(lex, rex, Loc, isCompAssign); return InvalidOperands(Loc, lex, rex); } @@ -7975,8 +6541,8 @@ inline QualType Sema::CheckLogicalOperands( // C99 6.5.[13,14] // is a constant. if (lex.get()->getType()->isIntegerType() && !lex.get()->getType()->isBooleanType() && rex.get()->getType()->isIntegerType() && !rex.get()->isValueDependent() && - // Don't warn in macros. - !Loc.isMacroID()) { + // Don't warn in macros or template instantiations. + !Loc.isMacroID() && ActiveTemplateInstantiations.empty()) { // If the RHS can be constant folded, and if it constant folds to something // that isn't 0 or 1 (which indicate a potential logical operation that // happened to fold to true/false) then warn. @@ -8099,7 +6665,43 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { unsigned Diag = 0; bool NeedType = false; switch (IsLV) { // C99 6.5.16p2 - case Expr::MLV_ConstQualified: Diag = diag::err_typecheck_assign_const; break; + case Expr::MLV_ConstQualified: + Diag = diag::err_typecheck_assign_const; + + // In ARC, use some specialized diagnostics for occasions where we + // infer 'const'. These are always pseudo-strong variables. + if (S.getLangOptions().ObjCAutoRefCount) { + DeclRefExpr *declRef = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()); + if (declRef && isa<VarDecl>(declRef->getDecl())) { + VarDecl *var = cast<VarDecl>(declRef->getDecl()); + + // Use the normal diagnostic if it's pseudo-__strong but the + // user actually wrote 'const'. + if (var->isARCPseudoStrong() && + (!var->getTypeSourceInfo() || + !var->getTypeSourceInfo()->getType().isConstQualified())) { + // There are two pseudo-strong cases: + // - self + ObjCMethodDecl *method = S.getCurMethodDecl(); + if (method && var == method->getSelfDecl()) + Diag = diag::err_typecheck_arr_assign_self; + + // - fast enumeration variables + else + Diag = diag::err_typecheck_arr_assign_enumeration; + + SourceRange Assign; + if (Loc != OrigLoc) + Assign = SourceRange(OrigLoc, OrigLoc); + S.Diag(Loc, Diag) << E->getSourceRange() << Assign; + // We need to preserve the AST regardless, so migration tool + // can do its job. + return false; + } + } + } + + break; case Expr::MLV_ArrayType: Diag = diag::err_typecheck_array_not_modifiable_lvalue; NeedType = true; @@ -8214,6 +6816,13 @@ QualType Sema::CheckAssignmentOperands(Expr *LHS, ExprResult &RHS, << SourceRange(UO->getOperatorLoc(), UO->getOperatorLoc()); } } + + if (ConvTy == Compatible) { + if (LHSType.getObjCLifetime() == Qualifiers::OCL_Strong) + checkRetainCycles(LHS, RHS.get()); + else if (getLangOptions().ObjCAutoRefCount) + checkUnsafeExprAssigns(Loc, LHS, RHS.get()); + } } else { // Compound assignment "x += y" ConvTy = CheckAssignmentConstraints(Loc, LHSType, RHSType); @@ -8295,29 +6904,9 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op, QualType PointeeTy = ResType->getPointeeType(); // C99 6.5.2.4p2, 6.5.6p2 - if (PointeeTy->isVoidType()) { - if (S.getLangOptions().CPlusPlus) { - S.Diag(OpLoc, diag::err_typecheck_pointer_arith_void_type) - << Op->getSourceRange(); - return QualType(); - } - - // Pointer to void is a GNU extension in C. - S.Diag(OpLoc, diag::ext_gnu_void_ptr) << Op->getSourceRange(); - } else if (PointeeTy->isFunctionType()) { - if (S.getLangOptions().CPlusPlus) { - S.Diag(OpLoc, diag::err_typecheck_pointer_arith_function_type) - << Op->getType() << Op->getSourceRange(); - return QualType(); - } - - S.Diag(OpLoc, diag::ext_gnu_ptr_func_arith) - << ResType << Op->getSourceRange(); - } else if (S.RequireCompleteType(OpLoc, PointeeTy, - S.PDiag(diag::err_typecheck_arithmetic_incomplete_type) - << Op->getSourceRange() - << ResType)) + if (!checkArithmeticOpPointerOperand(S, OpLoc, Op)) return QualType(); + // Diagnose bad cases where we step over interface counts. else if (PointeeTy->isObjCObjectType() && S.LangOpts.ObjCNonFragileABI) { S.Diag(OpLoc, diag::err_arithmetic_nonfragile_interface) @@ -8400,6 +6989,8 @@ void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS, QualType & LHS.get()->getObjectKind() == OK_ObjCProperty); const ObjCPropertyRefExpr *PropRef = LHS.get()->getObjCProperty(); + bool Consumed = false; + if (PropRef->isImplicitProperty()) { // If using property-dot syntax notation for assignment, and there is a // setter, RHS expression is being passed to the setter argument. So, @@ -8407,6 +6998,8 @@ void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS, QualType & if (const ObjCMethodDecl *SetterMD = PropRef->getImplicitPropertySetter()) { ObjCMethodDecl::param_iterator P = SetterMD->param_begin(); LHSTy = (*P)->getType(); + Consumed = (getLangOptions().ObjCAutoRefCount && + (*P)->hasAttr<NSConsumedAttr>()); // Otherwise, if the getter returns an l-value, just call that. } else { @@ -8418,14 +7011,26 @@ void Sema::ConvertPropertyForLValue(ExprResult &LHS, ExprResult &RHS, QualType & return; } } + } else if (getLangOptions().ObjCAutoRefCount) { + const ObjCMethodDecl *setter + = PropRef->getExplicitProperty()->getSetterMethodDecl(); + if (setter) { + ObjCMethodDecl::param_iterator P = setter->param_begin(); + LHSTy = (*P)->getType(); + Consumed = (*P)->hasAttr<NSConsumedAttr>(); + } } - if (getLangOptions().CPlusPlus && LHSTy->isRecordType()) { + if ((getLangOptions().CPlusPlus && LHSTy->isRecordType()) || + getLangOptions().ObjCAutoRefCount) { InitializedEntity Entity = - InitializedEntity::InitializeParameter(Context, LHSTy); + InitializedEntity::InitializeParameter(Context, LHSTy, Consumed); ExprResult ArgE = PerformCopyInitialization(Entity, SourceLocation(), RHS); - if (!ArgE.isInvalid()) + if (!ArgE.isInvalid()) { RHS = ArgE; + if (getLangOptions().ObjCAutoRefCount && !PropRef->isSuperReceiver()) + checkRetainCycles(const_cast<Expr*>(PropRef->getBase()), RHS.get()); + } } } @@ -8682,8 +7287,7 @@ static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK, VK = VK_LValue; // ...except that certain expressions are never l-values in C. - if (!S.getLangOptions().CPlusPlus && - IsCForbiddenLValueType(S.Context, Result)) + if (!S.getLangOptions().CPlusPlus && Result.isCForbiddenLValueType()) VK = VK_RValue; return Result; @@ -8815,6 +7419,53 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc, rhs = move(resolvedRHS); } + // The canonical way to check for a GNU null is with isNullPointerConstant, + // but we use a bit of a hack here for speed; this is a relatively + // hot path, and isNullPointerConstant is slow. + bool LeftNull = isa<GNUNullExpr>(lhs.get()->IgnoreParenImpCasts()); + bool RightNull = isa<GNUNullExpr>(rhs.get()->IgnoreParenImpCasts()); + + // Detect when a NULL constant is used improperly in an expression. These + // are mainly cases where the null pointer is used as an integer instead + // of a pointer. + if (LeftNull || RightNull) { + // Avoid analyzing cases where the result will either be invalid (and + // diagnosed as such) or entirely valid and not something to warn about. + QualType LeftType = lhs.get()->getType(); + QualType RightType = rhs.get()->getType(); + if (!LeftType->isBlockPointerType() && !LeftType->isMemberPointerType() && + !LeftType->isFunctionType() && + !RightType->isBlockPointerType() && + !RightType->isMemberPointerType() && + !RightType->isFunctionType()) { + if (Opc == BO_Mul || Opc == BO_Div || Opc == BO_Rem || Opc == BO_Add || + Opc == BO_Sub || Opc == BO_Shl || Opc == BO_Shr || Opc == BO_And || + Opc == BO_Xor || Opc == BO_Or || Opc == BO_MulAssign || + Opc == BO_DivAssign || Opc == BO_AddAssign || Opc == BO_SubAssign || + Opc == BO_RemAssign || Opc == BO_ShlAssign || Opc == BO_ShrAssign || + Opc == BO_AndAssign || Opc == BO_OrAssign || Opc == BO_XorAssign) { + // These are the operations that would not make sense with a null pointer + // no matter what the other expression is. + Diag(OpLoc, diag::warn_null_in_arithmetic_operation) + << (LeftNull ? lhs.get()->getSourceRange() : SourceRange()) + << (RightNull ? rhs.get()->getSourceRange() : SourceRange()); + } else if (Opc == BO_LE || Opc == BO_LT || Opc == BO_GE || Opc == BO_GT || + Opc == BO_EQ || Opc == BO_NE) { + // These are the operations that would not make sense with a null pointer + // if the other expression the other expression is not a pointer. + if (LeftNull != RightNull && + !LeftType->isAnyPointerType() && + !LeftType->canDecayToPointerType() && + !RightType->isAnyPointerType() && + !RightType->canDecayToPointerType()) { + Diag(OpLoc, diag::warn_null_in_arithmetic_operation) + << (LeftNull ? lhs.get()->getSourceRange() + : rhs.get()->getSourceRange()); + } + } + } + } + switch (Opc) { case BO_Assign: ResultTy = CheckAssignmentOperands(lhs.get(), rhs, OpLoc, QualType()); @@ -8953,28 +7604,46 @@ static void DiagnoseBitwisePrecedence(Sema &Self, BinaryOperatorKind Opc, (BinOp::isComparisonOp(rhsopc) || BinOp::isBitwiseOp(rhsopc))) return; - if (BinOp::isComparisonOp(lhsopc)) + if (BinOp::isComparisonOp(lhsopc)) { + Self.Diag(OpLoc, diag::warn_precedence_bitwise_rel) + << SourceRange(lhs->getLocStart(), OpLoc) + << BinOp::getOpcodeStr(Opc) << BinOp::getOpcodeStr(lhsopc); SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::warn_precedence_bitwise_rel) - << SourceRange(lhs->getLocStart(), OpLoc) - << BinOp::getOpcodeStr(Opc) << BinOp::getOpcodeStr(lhsopc), Self.PDiag(diag::note_precedence_bitwise_silence) << BinOp::getOpcodeStr(lhsopc), - lhs->getSourceRange(), + lhs->getSourceRange()); + SuggestParentheses(Self, OpLoc, Self.PDiag(diag::note_precedence_bitwise_first) << BinOp::getOpcodeStr(Opc), SourceRange(cast<BinOp>(lhs)->getRHS()->getLocStart(), rhs->getLocEnd())); - else if (BinOp::isComparisonOp(rhsopc)) + } else if (BinOp::isComparisonOp(rhsopc)) { + Self.Diag(OpLoc, diag::warn_precedence_bitwise_rel) + << SourceRange(OpLoc, rhs->getLocEnd()) + << BinOp::getOpcodeStr(Opc) << BinOp::getOpcodeStr(rhsopc); SuggestParentheses(Self, OpLoc, - Self.PDiag(diag::warn_precedence_bitwise_rel) - << SourceRange(OpLoc, rhs->getLocEnd()) - << BinOp::getOpcodeStr(Opc) << BinOp::getOpcodeStr(rhsopc), Self.PDiag(diag::note_precedence_bitwise_silence) << BinOp::getOpcodeStr(rhsopc), - rhs->getSourceRange(), + rhs->getSourceRange()); + SuggestParentheses(Self, OpLoc, Self.PDiag(diag::note_precedence_bitwise_first) << BinOp::getOpcodeStr(Opc), - SourceRange(lhs->getLocEnd(), cast<BinOp>(rhs)->getLHS()->getLocStart())); + SourceRange(lhs->getLocStart(), + cast<BinOp>(rhs)->getLHS()->getLocStart())); + } +} + +/// \brief It accepts a '&' expr that is inside a '|' one. +/// Emit a diagnostic together with a fixit hint that wraps the '&' expression +/// in parentheses. +static void +EmitDiagnosticForBitwiseAndInBitwiseOr(Sema &Self, SourceLocation OpLoc, + BinaryOperator *Bop) { + assert(Bop->getOpcode() == BO_And); + Self.Diag(Bop->getOperatorLoc(), diag::warn_bitwise_and_in_bitwise_or) + << Bop->getSourceRange() << OpLoc; + SuggestParentheses(Self, Bop->getOperatorLoc(), + Self.PDiag(diag::note_bitwise_and_in_bitwise_or_silence), + Bop->getSourceRange()); } /// \brief It accepts a '&&' expr that is inside a '||' one. @@ -8984,12 +7653,11 @@ static void EmitDiagnosticForLogicalAndInLogicalOr(Sema &Self, SourceLocation OpLoc, BinaryOperator *Bop) { assert(Bop->getOpcode() == BO_LAnd); + Self.Diag(Bop->getOperatorLoc(), diag::warn_logical_and_in_logical_or) + << Bop->getSourceRange() << OpLoc; SuggestParentheses(Self, Bop->getOperatorLoc(), - Self.PDiag(diag::warn_logical_and_in_logical_or) - << Bop->getSourceRange() << OpLoc, Self.PDiag(diag::note_logical_and_in_logical_or_silence), - Bop->getSourceRange(), - Self.PDiag(0), SourceRange()); + Bop->getSourceRange()); } /// \brief Returns true if the given expression can be evaluated as a constant @@ -9043,13 +7711,28 @@ static void DiagnoseLogicalAndInLogicalOrRHS(Sema &S, SourceLocation OpLoc, } } +/// \brief Look for '&' in the left or right hand of a '|' expr. +static void DiagnoseBitwiseAndInBitwiseOr(Sema &S, SourceLocation OpLoc, + Expr *OrArg) { + if (BinaryOperator *Bop = dyn_cast<BinaryOperator>(OrArg)) { + if (Bop->getOpcode() == BO_And) + return EmitDiagnosticForBitwiseAndInBitwiseOr(S, OpLoc, Bop); + } +} + /// DiagnoseBinOpPrecedence - Emit warnings for expressions with tricky /// precedence. static void DiagnoseBinOpPrecedence(Sema &Self, BinaryOperatorKind Opc, SourceLocation OpLoc, Expr *lhs, Expr *rhs){ // Diagnose "arg1 'bitwise' arg2 'eq' arg3". if (BinaryOperator::isBitwiseOp(Opc)) - return DiagnoseBitwisePrecedence(Self, Opc, OpLoc, lhs, rhs); + DiagnoseBitwisePrecedence(Self, Opc, OpLoc, lhs, rhs); + + // Diagnose "arg1 & arg2 | arg3" + if (Opc == BO_Or && !OpLoc.isMacroID()/* Don't warn in macros. */) { + DiagnoseBitwiseAndInBitwiseOr(Self, OpLoc, lhs); + DiagnoseBitwiseAndInBitwiseOr(Self, OpLoc, rhs); + } // Warn about arg1 || arg2 && arg3, as GCC 4.3+ does. // We don't warn for 'assert(a || b && "bad")' since this is safe. @@ -9274,6 +7957,29 @@ ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, Context.getPointerType(Context.VoidTy))); } +/// Given the last statement in a statement-expression, check whether +/// the result is a producing expression (like a call to an +/// ns_returns_retained function) and, if so, rebuild it to hoist the +/// release out of the full-expression. Otherwise, return null. +/// Cannot fail. +static Expr *maybeRebuildARCConsumingStmt(Stmt *s) { + // Should always be wrapped with one of these. + ExprWithCleanups *cleanups = dyn_cast<ExprWithCleanups>(s); + if (!cleanups) return 0; + + ImplicitCastExpr *cast = dyn_cast<ImplicitCastExpr>(cleanups->getSubExpr()); + if (!cast || cast->getCastKind() != CK_ObjCConsumeObject) + return 0; + + // Splice out the cast. This shouldn't modify any interesting + // features of the statement. + Expr *producer = cast->getSubExpr(); + assert(producer->getType() == cast->getType()); + assert(producer->getValueKind() == cast->getValueKind()); + cleanups->setSubExpr(producer); + return cleanups; +} + ExprResult Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, SourceLocation RPLoc) { // "({..})" @@ -9301,6 +8007,7 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, LastLabelStmt = Label; LastStmt = Label->getSubStmt(); } + if (Expr *LastE = dyn_cast<Expr>(LastStmt)) { // Do function/array conversion on the last expression, but not // lvalue-to-rvalue. However, initialize an unqualified type. @@ -9310,12 +8017,24 @@ Sema::ActOnStmtExpr(SourceLocation LPLoc, Stmt *SubStmt, Ty = LastExpr.get()->getType().getUnqualifiedType(); if (!Ty->isDependentType() && !LastExpr.get()->isTypeDependent()) { - LastExpr = PerformCopyInitialization( + // In ARC, if the final expression ends in a consume, splice + // the consume out and bind it later. In the alternate case + // (when dealing with a retainable type), the result + // initialization will create a produce. In both cases the + // result will be +1, and we'll need to balance that out with + // a bind. + if (Expr *rebuiltLastStmt + = maybeRebuildARCConsumingStmt(LastExpr.get())) { + LastExpr = rebuiltLastStmt; + } else { + LastExpr = PerformCopyInitialization( InitializedEntity::InitializeResult(LPLoc, Ty, false), SourceLocation(), - LastExpr); + LastExpr); + } + if (LastExpr.isInvalid()) return ExprError(); if (LastExpr.get() != 0) { @@ -9757,7 +8476,7 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, // If we don't have a function type, just build one from nothing. } else { FunctionProtoType::ExtProtoInfo EPI; - EPI.ExtInfo = FunctionType::ExtInfo(NoReturn, false, 0, CC_Default); + EPI.ExtInfo = FunctionType::ExtInfo().withNoReturn(NoReturn); BlockTy = Context.getFunctionType(RetTy, 0, 0, EPI); } @@ -9766,15 +8485,25 @@ ExprResult Sema::ActOnBlockStmtExpr(SourceLocation CaretLoc, BlockTy = Context.getBlockPointerType(BlockTy); // If needed, diagnose invalid gotos and switches in the block. - if (getCurFunction()->NeedsScopeChecking() && !hasAnyErrorsInThisFunction()) + if (getCurFunction()->NeedsScopeChecking() && + !hasAnyUnrecoverableErrorsInThisFunction()) DiagnoseInvalidJumps(cast<CompoundStmt>(Body)); BSI->TheDecl->setBody(cast<CompoundStmt>(Body)); - BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy); + for (BlockDecl::capture_const_iterator ci = BSI->TheDecl->capture_begin(), + ce = BSI->TheDecl->capture_end(); ci != ce; ++ci) { + const VarDecl *variable = ci->getVariable(); + QualType T = variable->getType(); + QualType::DestructionKind destructKind = T.isDestructedType(); + if (destructKind != QualType::DK_none) + getCurFunction()->setHasBranchProtectedScope(); + } + BlockExpr *Result = new (Context) BlockExpr(BSI->TheDecl, BlockTy); const AnalysisBasedWarnings::Policy &WP = AnalysisWarnings.getDefaultPolicy(); PopFunctionOrBlockScope(&WP, Result->getBlockDecl(), Result); + return Owned(Result); } @@ -9818,8 +8547,41 @@ ExprResult Sema::BuildVAArgExpr(SourceLocation BuiltinLoc, << OrigExpr->getType() << E->getSourceRange()); } - // FIXME: Check that type is complete/non-abstract - // FIXME: Warn if a non-POD type is passed in. + if (!TInfo->getType()->isDependentType()) { + if (RequireCompleteType(TInfo->getTypeLoc().getBeginLoc(), TInfo->getType(), + PDiag(diag::err_second_parameter_to_va_arg_incomplete) + << TInfo->getTypeLoc().getSourceRange())) + return ExprError(); + + if (RequireNonAbstractType(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getType(), + PDiag(diag::err_second_parameter_to_va_arg_abstract) + << TInfo->getTypeLoc().getSourceRange())) + return ExprError(); + + if (!TInfo->getType().isPODType(Context)) + Diag(TInfo->getTypeLoc().getBeginLoc(), + diag::warn_second_parameter_to_va_arg_not_pod) + << TInfo->getType() + << TInfo->getTypeLoc().getSourceRange(); + + // Check for va_arg where arguments of the given type will be promoted + // (i.e. this va_arg is guaranteed to have undefined behavior). + QualType PromoteType; + if (TInfo->getType()->isPromotableIntegerType()) { + PromoteType = Context.getPromotedIntegerType(TInfo->getType()); + if (Context.typesAreCompatible(PromoteType, TInfo->getType())) + PromoteType = QualType(); + } + if (TInfo->getType()->isSpecificBuiltinType(BuiltinType::Float)) + PromoteType = Context.DoubleTy; + if (!PromoteType.isNull()) + Diag(TInfo->getTypeLoc().getBeginLoc(), + diag::warn_second_parameter_to_va_arg_never_compatible) + << TInfo->getType() + << PromoteType + << TInfo->getTypeLoc().getSourceRange(); + } QualType T = TInfo->getType().getNonLValueExprType(Context); return Owned(new (Context) VAArgExpr(BuiltinLoc, E, TInfo, RPLoc, T)); @@ -9913,6 +8675,11 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, if (lhq.getAddressSpace() != rhq.getAddressSpace()) { DiagKind = diag::err_typecheck_incompatible_address_space; break; + + + } else if (lhq.getObjCLifetime() != rhq.getObjCLifetime()) { + DiagKind = diag::err_typecheck_incompatible_ownership; + break; } llvm_unreachable("unknown error case for discarding qualifiers!"); @@ -9950,6 +8717,9 @@ bool Sema::DiagnoseAssignmentResult(AssignConvertType ConvTy, case IncompatibleVectors: DiagKind = diag::warn_incompatible_vectors; break; + case IncompatibleObjCWeakRef: + DiagKind = diag::err_arc_weak_unavailable_assign; + break; case Incompatible: DiagKind = diag::err_typecheck_convert_incompatible; isInvalid = true; @@ -10027,7 +8797,10 @@ bool Sema::VerifyIntegerConstantExpression(const Expr *E, llvm::APSInt *Result){ void Sema::PushExpressionEvaluationContext(ExpressionEvaluationContext NewContext) { ExprEvalContexts.push_back( - ExpressionEvaluationContextRecord(NewContext, ExprTemporaries.size())); + ExpressionEvaluationContextRecord(NewContext, + ExprTemporaries.size(), + ExprNeedsCleanups)); + ExprNeedsCleanups = false; } void @@ -10062,15 +8835,27 @@ Sema::PopExpressionEvaluationContext() { // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they // will never be constructed. - if (Rec.Context == Unevaluated && - ExprTemporaries.size() > Rec.NumTemporaries) + if (Rec.Context == Unevaluated) { ExprTemporaries.erase(ExprTemporaries.begin() + Rec.NumTemporaries, ExprTemporaries.end()); + ExprNeedsCleanups = Rec.ParentNeedsCleanups; + + // Otherwise, merge the contexts together. + } else { + ExprNeedsCleanups |= Rec.ParentNeedsCleanups; + } // Destroy the popped expression evaluation record. Rec.Destroy(); } +void Sema::DiscardCleanupsInEvaluationContext() { + ExprTemporaries.erase( + ExprTemporaries.begin() + ExprEvalContexts.back().NumTemporaries, + ExprTemporaries.end()); + ExprNeedsCleanups = false; +} + /// \brief Note that the given declaration was referenced in the source code. /// /// This routine should be invoke whenever a given declaration is referenced @@ -10364,7 +9149,7 @@ void Sema::MarkDeclarationsReferencedInExpr(Expr *E) { /// during overload resolution or within sizeof/alignof/typeof/typeid. bool Sema::DiagRuntimeBehavior(SourceLocation Loc, const Stmt *stmt, const PartialDiagnostic &PD) { - switch (ExprEvalContexts.back().Context ) { + switch (ExprEvalContexts.back().Context) { case Unevaluated: // The argument will never be evaluated, so don't complain. break; @@ -10780,9 +9565,6 @@ ExprResult RebuildUnknownAnyExpr::VisitCallExpr(CallExpr *call) { } ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *msg) { - ObjCMethodDecl *method = msg->getMethodDecl(); - assert(method && "__unknown_anytype message without result type?"); - // Verify that this is a legal result type of a call. if (DestType->isArrayType() || DestType->isFunctionType()) { S.Diag(msg->getExprLoc(), diag::err_func_returning_array_function) @@ -10790,8 +9572,11 @@ ExprResult RebuildUnknownAnyExpr::VisitObjCMessageExpr(ObjCMessageExpr *msg) { return ExprError(); } - assert(method->getResultType() == S.Context.UnknownAnyTy); - method->setResultType(DestType); + // Rewrite the method result type if available. + if (ObjCMethodDecl *method = msg->getMethodDecl()) { + assert(method->getResultType() == S.Context.UnknownAnyTy); + method->setResultType(DestType); + } // Change the type of the message. msg->setType(DestType.getNonReferenceType()); diff --git a/lib/Sema/SemaExprCXX.cpp b/lib/Sema/SemaExprCXX.cpp index 2f5a890e51de..e804fcd60c74 100644 --- a/lib/Sema/SemaExprCXX.cpp +++ b/lib/Sema/SemaExprCXX.cpp @@ -480,23 +480,63 @@ Sema::ActOnCXXNullPtrLiteral(SourceLocation Loc) { /// ActOnCXXThrow - Parse throw expressions. ExprResult -Sema::ActOnCXXThrow(SourceLocation OpLoc, Expr *Ex) { +Sema::ActOnCXXThrow(Scope *S, SourceLocation OpLoc, Expr *Ex) { + bool IsThrownVarInScope = false; + if (Ex) { + // C++0x [class.copymove]p31: + // When certain criteria are met, an implementation is allowed to omit the + // copy/move construction of a class object [...] + // + // - in a throw-expression, when the operand is the name of a + // non-volatile automatic object (other than a function or catch- + // clause parameter) whose scope does not extend beyond the end of the + // innermost enclosing try-block (if there is one), the copy/move + // operation from the operand to the exception object (15.1) can be + // omitted by constructing the automatic object directly into the + // exception object + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex->IgnoreParens())) + if (VarDecl *Var = dyn_cast<VarDecl>(DRE->getDecl())) { + if (Var->hasLocalStorage() && !Var->getType().isVolatileQualified()) { + for( ; S; S = S->getParent()) { + if (S->isDeclScope(Var)) { + IsThrownVarInScope = true; + break; + } + + if (S->getFlags() & + (Scope::FnScope | Scope::ClassScope | Scope::BlockScope | + Scope::FunctionPrototypeScope | Scope::ObjCMethodScope | + Scope::TryScope)) + break; + } + } + } + } + + return BuildCXXThrow(OpLoc, Ex, IsThrownVarInScope); +} + +ExprResult Sema::BuildCXXThrow(SourceLocation OpLoc, Expr *Ex, + bool IsThrownVarInScope) { // Don't report an error if 'throw' is used in system headers. if (!getLangOptions().CXXExceptions && !getSourceManager().isInSystemHeader(OpLoc)) Diag(OpLoc, diag::err_exceptions_disabled) << "throw"; - + if (Ex && !Ex->isTypeDependent()) { - ExprResult ExRes = CheckCXXThrowOperand(OpLoc, Ex); + ExprResult ExRes = CheckCXXThrowOperand(OpLoc, Ex, IsThrownVarInScope); if (ExRes.isInvalid()) return ExprError(); Ex = ExRes.take(); } - return Owned(new (Context) CXXThrowExpr(Ex, Context.VoidTy, OpLoc)); + + return Owned(new (Context) CXXThrowExpr(Ex, Context.VoidTy, OpLoc, + IsThrownVarInScope)); } /// CheckCXXThrowOperand - Validate the operand of a throw. -ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E) { +ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E, + bool IsThrownVarInScope) { // C++ [except.throw]p3: // A throw-expression initializes a temporary object, called the exception // object, the type of which is determined by removing any top-level @@ -535,14 +575,28 @@ ExprResult Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *E) { // Initialize the exception result. This implicitly weeds out // abstract types or types with inaccessible copy constructors. - const VarDecl *NRVOVariable = getCopyElisionCandidate(QualType(), E, false); - - // FIXME: Determine whether we can elide this copy per C++0x [class.copy]p32. + + // C++0x [class.copymove]p31: + // When certain criteria are met, an implementation is allowed to omit the + // copy/move construction of a class object [...] + // + // - in a throw-expression, when the operand is the name of a + // non-volatile automatic object (other than a function or catch-clause + // parameter) whose scope does not extend beyond the end of the + // innermost enclosing try-block (if there is one), the copy/move + // operation from the operand to the exception object (15.1) can be + // omitted by constructing the automatic object directly into the + // exception object + const VarDecl *NRVOVariable = 0; + if (IsThrownVarInScope) + NRVOVariable = getCopyElisionCandidate(QualType(), E, false); + InitializedEntity Entity = InitializedEntity::InitializeException(ThrowLoc, E->getType(), - /*NRVO=*/false); + /*NRVO=*/NRVOVariable != 0); Res = PerformMoveOrCopyInitialization(Entity, NRVOVariable, - QualType(), E); + QualType(), E, + IsThrownVarInScope); if (Res.isInvalid()) return ExprError(); E = Res.take(); @@ -691,7 +745,8 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, ExprValueKind VK = VK_RValue; CXXCastPath BasePath; ExprResult CastExpr = - CheckCastTypes(TInfo->getTypeLoc().getSourceRange(), Ty, Exprs[0], + CheckCastTypes(TInfo->getTypeLoc().getBeginLoc(), + TInfo->getTypeLoc().getSourceRange(), Ty, Exprs[0], Kind, VK, BasePath, /*FunctionalStyle=*/true); if (CastExpr.isInvalid()) @@ -827,8 +882,7 @@ Sema::ActOnCXXNew(SourceLocation StartLoc, bool UseGlobal, } } - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, /*Scope=*/0, /*OwnedDecl=*/0, - /*AllowAuto=*/true); + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, /*Scope=*/0); QualType AllocType = TInfo->getType(); if (D.isInvalidType()) return ExprError(); @@ -902,8 +956,16 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, if (CheckAllocatedType(AllocType, TypeRange.getBegin(), TypeRange)) return ExprError(); - QualType ResultType = Context.getPointerType(AllocType); + // In ARC, infer 'retaining' for the allocated + if (getLangOptions().ObjCAutoRefCount && + AllocType.getObjCLifetime() == Qualifiers::OCL_None && + AllocType->isObjCLifetimeType()) { + AllocType = Context.getLifetimeQualifiedType(AllocType, + AllocType->getObjCARCImplicitLifetime()); + } + QualType ResultType = Context.getPointerType(AllocType); + // C++ 5.3.4p6: "The expression in a direct-new-declarator shall have integral // or enumeration type with a non-negative value." if (ArraySize && !ArraySize->isTypeDependent()) { @@ -964,6 +1026,14 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, } } + // ARC: warn about ABI issues. + if (getLangOptions().ObjCAutoRefCount) { + QualType BaseAllocType = Context.getBaseElementType(AllocType); + if (BaseAllocType.hasStrongOrWeakObjCLifetime()) + Diag(StartLoc, diag::warn_err_new_delete_object_array) + << 0 << BaseAllocType; + } + // Note that we do *not* convert the argument in any way. It can // be signed, larger than size_t, whatever. } @@ -1078,7 +1148,17 @@ Sema::BuildCXXNew(SourceLocation StartLoc, bool UseGlobal, if (OperatorDelete) MarkDeclarationReferenced(StartLoc, OperatorDelete); - // FIXME: Also check that the destructor is accessible. (C++ 5.3.4p16) + // C++0x [expr.new]p17: + // If the new expression creates an array of objects of class type, + // access and ambiguity control are done for the destructor. + if (ArraySize && Constructor) { + if (CXXDestructorDecl *dtor = LookupDestructor(Constructor->getParent())) { + MarkDeclarationReferenced(StartLoc, dtor); + CheckDestructorAccess(StartLoc, dtor, + PDiag(diag::err_access_dtor) + << Context.getBaseElementType(AllocType)); + } + } PlacementArgs.release(); ConstructorArgs.release(); @@ -1122,6 +1202,15 @@ bool Sema::CheckAllocatedType(QualType AllocType, SourceLocation Loc, else if (unsigned AddressSpace = AllocType.getAddressSpace()) return Diag(Loc, diag::err_address_space_qualified_new) << AllocType.getUnqualifiedType() << AddressSpace; + else if (getLangOptions().ObjCAutoRefCount) { + if (const ArrayType *AT = Context.getAsArrayType(AllocType)) { + QualType BaseAllocType = Context.getBaseElementType(AT); + if (BaseAllocType.getObjCLifetime() == Qualifiers::OCL_None && + BaseAllocType->isObjCLifetimeType()) + return Diag(Loc, diag::err_arc_new_array_without_ownership) + << BaseAllocType; + } + } return false; } @@ -1774,8 +1863,9 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, // delete-expression; it is not necessary to cast away the constness // (5.2.11) of the pointer expression before it is used as the operand // of the delete-expression. ] - Ex = ImpCastExprToType(Ex.take(), Context.getPointerType(Context.VoidTy), - CK_NoOp); + if (!Context.hasSameType(Ex.get()->getType(), Context.VoidPtrTy)) + Ex = Owned(ImplicitCastExpr::Create(Context, Context.VoidPtrTy, CK_NoOp, + Ex.take(), 0, VK_RValue)); if (Pointee->isArrayType() && !ArrayForm) { Diag(StartLoc, diag::warn_delete_array_type) @@ -1830,6 +1920,14 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal, if (!dtor || !dtor->isVirtual()) Diag(StartLoc, diag::warn_delete_non_virtual_dtor) << PointeeElem; } + + } else if (getLangOptions().ObjCAutoRefCount && + PointeeElem->isObjCLifetimeType() && + (PointeeElem.getObjCLifetime() == Qualifiers::OCL_Strong || + PointeeElem.getObjCLifetime() == Qualifiers::OCL_Weak) && + ArrayForm) { + Diag(StartLoc, diag::warn_err_new_delete_object_array) + << 1 << PointeeElem; } if (!OperatorDelete) { @@ -1988,11 +2086,12 @@ static ExprResult BuildCXXCastArgument(Sema &S, ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, const ImplicitConversionSequence &ICS, - AssignmentAction Action, bool CStyle) { + AssignmentAction Action, + CheckedConversionKind CCK) { switch (ICS.getKind()) { case ImplicitConversionSequence::StandardConversion: { ExprResult Res = PerformImplicitConversion(From, ToType, ICS.Standard, - Action, CStyle); + Action, CCK); if (Res.isInvalid()) return ExprError(); From = Res.take(); @@ -2027,7 +2126,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ExprResult Res = PerformImplicitConversion(From, BeforeToType, ICS.UserDefined.Before, AA_Converting, - CStyle); + CCK); if (Res.isInvalid()) return ExprError(); From = Res.take(); @@ -2047,7 +2146,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, From = CastArg.take(); return PerformImplicitConversion(From, ToType, ICS.UserDefined.After, - AA_Converting, CStyle); + AA_Converting, CCK); } case ImplicitConversionSequence::AmbiguousConversion: @@ -2076,13 +2175,16 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, const StandardConversionSequence& SCS, - AssignmentAction Action, bool CStyle) { + AssignmentAction Action, + CheckedConversionKind CCK) { + bool CStyle = (CCK == CCK_CStyleCast || CCK == CCK_FunctionalCast); + // Overall FIXME: we are recomputing too many types here and doing far too // much extra work. What this means is that we need to keep track of more // information that is computed when we try the implicit conversion initially, // so that we don't need to recompute anything here. QualType FromType = From->getType(); - + if (SCS.CopyConstructor) { // FIXME: When can ToType be a reference type? assert(!ToType->isReferenceType()); @@ -2149,12 +2251,14 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, case ICK_Array_To_Pointer: FromType = Context.getArrayDecayedType(FromType); - From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay).take(); + From = ImpCastExprToType(From, FromType, CK_ArrayToPointerDecay, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Function_To_Pointer: FromType = Context.getPointerType(FromType); - From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay).take(); + From = ImpCastExprToType(From, FromType, CK_FunctionToPointerDecay, + VK_RValue, /*BasePath=*/0, CCK).take(); break; default: @@ -2178,17 +2282,20 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, if (CheckExceptionSpecCompatibility(From, ToType)) return ExprError(); - From = ImpCastExprToType(From, ToType, CK_NoOp).take(); + From = ImpCastExprToType(From, ToType, CK_NoOp, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Integral_Promotion: case ICK_Integral_Conversion: - From = ImpCastExprToType(From, ToType, CK_IntegralCast).take(); + From = ImpCastExprToType(From, ToType, CK_IntegralCast, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Floating_Promotion: case ICK_Floating_Conversion: - From = ImpCastExprToType(From, ToType, CK_FloatingCast).take(); + From = ImpCastExprToType(From, ToType, CK_FloatingCast, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Complex_Promotion: @@ -2206,21 +2313,26 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, } else { CK = CK_IntegralComplexCast; } - From = ImpCastExprToType(From, ToType, CK).take(); + From = ImpCastExprToType(From, ToType, CK, + VK_RValue, /*BasePath=*/0, CCK).take(); break; } case ICK_Floating_Integral: if (ToType->isRealFloatingType()) - From = ImpCastExprToType(From, ToType, CK_IntegralToFloating).take(); + From = ImpCastExprToType(From, ToType, CK_IntegralToFloating, + VK_RValue, /*BasePath=*/0, CCK).take(); else - From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral).take(); + From = ImpCastExprToType(From, ToType, CK_FloatingToIntegral, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Compatible_Conversion: - From = ImpCastExprToType(From, ToType, CK_NoOp).take(); + From = ImpCastExprToType(From, ToType, CK_NoOp, + VK_RValue, /*BasePath=*/0, CCK).take(); break; + case ICK_Writeback_Conversion: case ICK_Pointer_Conversion: { if (SCS.IncompatibleObjC && Action != AA_Casting) { // Diagnose incompatible Objective-C conversions @@ -2234,17 +2346,30 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, diag::ext_typecheck_convert_incompatible_pointer) << From->getType() << ToType << Action << From->getSourceRange(); - + if (From->getType()->isObjCObjectPointerType() && ToType->isObjCObjectPointerType()) EmitRelatedResultTypeNote(From); - } - + } + else if (getLangOptions().ObjCAutoRefCount && + !CheckObjCARCUnavailableWeakConversion(ToType, + From->getType())) { + if (Action == AA_Initializing) + Diag(From->getSourceRange().getBegin(), + diag::err_arc_weak_unavailable_assign); + else + Diag(From->getSourceRange().getBegin(), + diag::err_arc_convesion_of_weak_unavailable) + << (Action == AA_Casting) << From->getType() << ToType + << From->getSourceRange(); + } + CastKind Kind = CK_Invalid; CXXCastPath BasePath; if (CheckPointerConversion(From, ToType, Kind, BasePath, CStyle)) return ExprError(); - From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath).take(); + From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath, CCK) + .take(); break; } @@ -2255,13 +2380,15 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, return ExprError(); if (CheckExceptionSpecCompatibility(From, ToType)) return ExprError(); - From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath).take(); + From = ImpCastExprToType(From, ToType, Kind, VK_RValue, &BasePath, CCK) + .take(); break; } case ICK_Boolean_Conversion: From = ImpCastExprToType(From, Context.BoolTy, - ScalarTypeToBooleanCastKind(FromType)).take(); + ScalarTypeToBooleanCastKind(FromType), + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Derived_To_Base: { @@ -2276,16 +2403,18 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, From = ImpCastExprToType(From, ToType.getNonReferenceType(), CK_DerivedToBase, CastCategory(From), - &BasePath).take(); + &BasePath, CCK).take(); break; } case ICK_Vector_Conversion: - From = ImpCastExprToType(From, ToType, CK_BitCast).take(); + From = ImpCastExprToType(From, ToType, CK_BitCast, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Vector_Splat: - From = ImpCastExprToType(From, ToType, CK_VectorSplat).take(); + From = ImpCastExprToType(From, ToType, CK_VectorSplat, + VK_RValue, /*BasePath=*/0, CCK).take(); break; case ICK_Complex_Real: @@ -2321,27 +2450,30 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, // _Complex x -> x From = ImpCastExprToType(From, ElType, isFloatingComplex ? CK_FloatingComplexToReal - : CK_IntegralComplexToReal).take(); + : CK_IntegralComplexToReal, + VK_RValue, /*BasePath=*/0, CCK).take(); // x -> y if (Context.hasSameUnqualifiedType(ElType, ToType)) { // do nothing } else if (ToType->isRealFloatingType()) { From = ImpCastExprToType(From, ToType, - isFloatingComplex ? CK_FloatingCast : CK_IntegralToFloating).take(); + isFloatingComplex ? CK_FloatingCast : CK_IntegralToFloating, + VK_RValue, /*BasePath=*/0, CCK).take(); } else { assert(ToType->isIntegerType()); From = ImpCastExprToType(From, ToType, - isFloatingComplex ? CK_FloatingToIntegral : CK_IntegralCast).take(); + isFloatingComplex ? CK_FloatingToIntegral : CK_IntegralCast, + VK_RValue, /*BasePath=*/0, CCK).take(); } } break; case ICK_Block_Pointer_Conversion: { - From = ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast, - VK_RValue).take(); - break; - } + From = ImpCastExprToType(From, ToType.getUnqualifiedType(), CK_BitCast, + VK_RValue, /*BasePath=*/0, CCK).take(); + break; + } case ICK_TransparentUnionConversion: { ExprResult FromRes = Owned(From); @@ -2376,7 +2508,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType, ExprValueKind VK = ToType->isReferenceType() ? CastCategory(From) : VK_RValue; From = ImpCastExprToType(From, ToType.getNonLValueExprType(Context), - CK_NoOp, VK).take(); + CK_NoOp, VK, /*BasePath=*/0, CCK).take(); if (SCS.DeprecatedStringLiteralToCharPtr && !getLangOptions().WritableStrings) @@ -2553,6 +2685,23 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, case UTT_IsObject: return T->isObjectType(); case UTT_IsScalar: + // Note: semantic analysis depends on Objective-C lifetime types to be + // considered scalar types. However, such types do not actually behave + // like scalar types at run time (since they may require retain/release + // operations), so we report them as non-scalar. + if (T->isObjCLifetimeType()) { + switch (T.getObjCLifetime()) { + case Qualifiers::OCL_None: + case Qualifiers::OCL_ExplicitNone: + return true; + + case Qualifiers::OCL_Strong: + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Autoreleasing: + return false; + } + } + return T->isScalarType(); case UTT_IsCompound: return T->isCompoundType(); @@ -2566,13 +2715,13 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, case UTT_IsVolatile: return T.isVolatileQualified(); case UTT_IsTrivial: - return T->isTrivialType(); + return T.isTrivialType(Self.Context); case UTT_IsTriviallyCopyable: - return T->isTriviallyCopyableType(); + return T.isTriviallyCopyableType(Self.Context); case UTT_IsStandardLayout: return T->isStandardLayoutType(); case UTT_IsPOD: - return T->isPODType(); + return T.isPODType(Self.Context); case UTT_IsLiteral: return T->isLiteralType(); case UTT_IsEmpty: @@ -2605,7 +2754,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // If __is_pod (type) is true then the trait is true, else if type is // a cv class or union type (or array thereof) with a trivial default // constructor ([class.ctor]) then the trait is true, else it is false. - if (T->isPODType()) + if (T.isPODType(Self.Context)) return true; if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) @@ -2617,7 +2766,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // the trait is true, else if type is a cv class or union type // with a trivial copy constructor ([class.copy]) then the trait // is true, else it is false. - if (T->isPODType() || T->isReferenceType()) + if (T.isPODType(Self.Context) || T->isReferenceType()) return true; if (const RecordType *RT = T->getAs<RecordType>()) return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyConstructor(); @@ -2637,7 +2786,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, if (C.getBaseElementType(T).isConstQualified()) return false; - if (T->isPODType()) + if (T.isPODType(Self.Context)) return true; if (const RecordType *RT = T->getAs<RecordType>()) return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialCopyAssignment(); @@ -2649,8 +2798,14 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // type (or array thereof) with a trivial destructor // ([class.dtor]) then the trait is true, else it is // false. - if (T->isPODType() || T->isReferenceType()) + if (T.isPODType(Self.Context) || T->isReferenceType()) + return true; + + // Objective-C++ ARC: autorelease types don't require destruction. + if (T->isObjCLifetimeType() && + T.getObjCLifetime() == Qualifiers::OCL_Autoreleasing) return true; + if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) return cast<CXXRecordDecl>(RT->getDecl())->hasTrivialDestructor(); @@ -2668,8 +2823,8 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, return false; if (T->isReferenceType()) return false; - if (T->isPODType()) - return true; + if (T.isPODType(Self.Context) || T->isObjCLifetimeType()) + return true; if (const RecordType *RT = T->getAs<RecordType>()) { CXXRecordDecl* RD = cast<CXXRecordDecl>(RT->getDecl()); if (RD->hasTrivialCopyAssignment()) @@ -2704,7 +2859,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // if type is a cv class or union type with copy constructors that are // known not to throw an exception then the trait is true, else it is // false. - if (T->isPODType() || T->isReferenceType()) + if (T.isPODType(C) || T->isReferenceType() || T->isObjCLifetimeType()) return true; if (const RecordType *RT = T->getAs<RecordType>()) { CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); @@ -2744,7 +2899,7 @@ static bool EvaluateUnaryTypeTrait(Sema &Self, UnaryTypeTrait UTT, // true, else if type is a cv class or union type (or array // thereof) with a default constructor that is known not to // throw an exception then the trait is true, else it is false. - if (T->isPODType()) + if (T.isPODType(C) || T->isObjCLifetimeType()) return true; if (const RecordType *RT = C.getBaseElementType(T)->getAs<RecordType>()) { CXXRecordDecl *RD = cast<CXXRecordDecl>(RT->getDecl()); @@ -3089,6 +3244,20 @@ QualType Sema::CheckPointerToMemberOperands(ExprResult &lex, ExprResult &rex, ExprValueKind &VK, SourceLocation Loc, bool isIndirect) { + assert(!lex.get()->getType()->isPlaceholderType() && + !rex.get()->getType()->isPlaceholderType() && + "placeholders should have been weeded out by now"); + + // The LHS undergoes lvalue conversions if this is ->*. + if (isIndirect) { + lex = DefaultLvalueConversion(lex.take()); + if (lex.isInvalid()) return QualType(); + } + + // The RHS always undergoes lvalue conversions. + rex = DefaultLvalueConversion(rex.take()); + if (rex.isInvalid()) return QualType(); + const char *OpSpelling = isIndirect ? "->*" : ".*"; // C++ 5.5p2 // The binary operator .* [p3: ->*] binds its second operand, which shall @@ -3556,7 +3725,7 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult &Cond, ExprResult &LHS, Ex // Extension: conditional operator involving vector types. if (LTy->isVectorType() || RTy->isVectorType()) - return CheckVectorOperands(QuestionLoc, LHS, RHS); + return CheckVectorOperands(LHS, RHS, QuestionLoc, /*isCompAssign*/false); // -- The second and third operands have arithmetic or enumeration type; // the usual arithmetic conversions are performed to bring them to a @@ -3828,17 +3997,83 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { if (!E) return ExprError(); - if (!Context.getLangOptions().CPlusPlus) + assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?"); + + // If the result is a glvalue, we shouldn't bind it. + if (!E->isRValue()) return Owned(E); - assert(!isa<CXXBindTemporaryExpr>(E) && "Double-bound temporary?"); + // In ARC, calls that return a retainable type can return retained, + // in which case we have to insert a consuming cast. + if (getLangOptions().ObjCAutoRefCount && + E->getType()->isObjCRetainableType()) { + + bool ReturnsRetained; + + // For actual calls, we compute this by examining the type of the + // called value. + if (CallExpr *Call = dyn_cast<CallExpr>(E)) { + Expr *Callee = Call->getCallee()->IgnoreParens(); + QualType T = Callee->getType(); + + if (T == Context.BoundMemberTy) { + // Handle pointer-to-members. + if (BinaryOperator *BinOp = dyn_cast<BinaryOperator>(Callee)) + T = BinOp->getRHS()->getType(); + else if (MemberExpr *Mem = dyn_cast<MemberExpr>(Callee)) + T = Mem->getMemberDecl()->getType(); + } + + if (const PointerType *Ptr = T->getAs<PointerType>()) + T = Ptr->getPointeeType(); + else if (const BlockPointerType *Ptr = T->getAs<BlockPointerType>()) + T = Ptr->getPointeeType(); + else if (const MemberPointerType *MemPtr = T->getAs<MemberPointerType>()) + T = MemPtr->getPointeeType(); + + const FunctionType *FTy = T->getAs<FunctionType>(); + assert(FTy && "call to value not of function type?"); + ReturnsRetained = FTy->getExtInfo().getProducesResult(); + + // ActOnStmtExpr arranges things so that StmtExprs of retainable + // type always produce a +1 object. + } else if (isa<StmtExpr>(E)) { + ReturnsRetained = true; + + // For message sends and property references, we try to find an + // actual method. FIXME: we should infer retention by selector in + // cases where we don't have an actual method. + } else { + Decl *D = 0; + if (ObjCMessageExpr *Send = dyn_cast<ObjCMessageExpr>(E)) { + D = Send->getMethodDecl(); + } else { + CastExpr *CE = cast<CastExpr>(E); + // FIXME. What other cast kinds to check for? + if (CE->getCastKind() == CK_ObjCProduceObject || + CE->getCastKind() == CK_LValueToRValue) + return MaybeBindToTemporary(CE->getSubExpr()); + assert(CE->getCastKind() == CK_GetObjCProperty); + const ObjCPropertyRefExpr *PRE = CE->getSubExpr()->getObjCProperty(); + D = (PRE->isImplicitProperty() ? PRE->getImplicitPropertyGetter() : 0); + } - const RecordType *RT = E->getType()->getAs<RecordType>(); - if (!RT) + ReturnsRetained = (D && D->hasAttr<NSReturnsRetainedAttr>()); + } + + ExprNeedsCleanups = true; + + CastKind ck = (ReturnsRetained ? CK_ObjCConsumeObject + : CK_ObjCReclaimReturnedObject); + return Owned(ImplicitCastExpr::Create(Context, E->getType(), ck, E, 0, + VK_RValue)); + } + + if (!getLangOptions().CPlusPlus) return Owned(E); - // If the result is a glvalue, we shouldn't bind it. - if (E->Classify(Context).isGLValue()) + const RecordType *RT = E->getType()->getAs<RecordType>(); + if (!RT) return Owned(E); // That should be enough to guarantee that this type is complete. @@ -3847,15 +4082,18 @@ ExprResult Sema::MaybeBindToTemporary(Expr *E) { if (RD->isInvalidDecl() || RD->hasTrivialDestructor()) return Owned(E); - CXXTemporary *Temp = CXXTemporary::Create(Context, LookupDestructor(RD)); - ExprTemporaries.push_back(Temp); - if (CXXDestructorDecl *Destructor = LookupDestructor(RD)) { + CXXDestructorDecl *Destructor = LookupDestructor(RD); + + CXXTemporary *Temp = CXXTemporary::Create(Context, Destructor); + if (Destructor) { MarkDeclarationReferenced(E->getExprLoc(), Destructor); CheckDestructorAccess(E->getExprLoc(), Destructor, PDiag(diag::err_access_dtor_temp) << E->getType()); + + ExprTemporaries.push_back(Temp); + ExprNeedsCleanups = true; } - // FIXME: Add the temporary to the temporaries vector. return Owned(CXXBindTemporaryExpr::Create(Context, Temp, E)); } @@ -3864,14 +4102,16 @@ Expr *Sema::MaybeCreateExprWithCleanups(Expr *SubExpr) { unsigned FirstTemporary = ExprEvalContexts.back().NumTemporaries; assert(ExprTemporaries.size() >= FirstTemporary); - if (ExprTemporaries.size() == FirstTemporary) + assert(ExprNeedsCleanups || ExprTemporaries.size() == FirstTemporary); + if (!ExprNeedsCleanups) return SubExpr; Expr *E = ExprWithCleanups::Create(Context, SubExpr, - &ExprTemporaries[FirstTemporary], + ExprTemporaries.begin() + FirstTemporary, ExprTemporaries.size() - FirstTemporary); ExprTemporaries.erase(ExprTemporaries.begin() + FirstTemporary, ExprTemporaries.end()); + ExprNeedsCleanups = false; return E; } @@ -3887,9 +4127,7 @@ Sema::MaybeCreateExprWithCleanups(ExprResult SubExpr) { Stmt *Sema::MaybeCreateStmtWithCleanups(Stmt *SubStmt) { assert(SubStmt && "sub statement can't be null!"); - unsigned FirstTemporary = ExprEvalContexts.back().NumTemporaries; - assert(ExprTemporaries.size() >= FirstTemporary); - if (ExprTemporaries.size() == FirstTemporary) + if (!ExprNeedsCleanups) return SubStmt; // FIXME: In order to attach the temporaries, wrap the statement into @@ -4047,17 +4285,35 @@ ExprResult Sema::BuildPseudoDestructorExpr(Expr *Base, QualType DestructedType = DestructedTypeInfo->getType(); SourceLocation DestructedTypeStart = DestructedTypeInfo->getTypeLoc().getLocalSourceRange().getBegin(); - if (!DestructedType->isDependentType() && !ObjectType->isDependentType() && - !Context.hasSameUnqualifiedType(DestructedType, ObjectType)) { - Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch) - << ObjectType << DestructedType << Base->getSourceRange() - << DestructedTypeInfo->getTypeLoc().getLocalSourceRange(); - - // Recover by setting the destructed type to the object type. - DestructedType = ObjectType; - DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType, + if (!DestructedType->isDependentType() && !ObjectType->isDependentType()) { + if (!Context.hasSameUnqualifiedType(DestructedType, ObjectType)) { + Diag(DestructedTypeStart, diag::err_pseudo_dtor_type_mismatch) + << ObjectType << DestructedType << Base->getSourceRange() + << DestructedTypeInfo->getTypeLoc().getLocalSourceRange(); + + // Recover by setting the destructed type to the object type. + DestructedType = ObjectType; + DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType, + DestructedTypeStart); + Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo); + } else if (DestructedType.getObjCLifetime() != + ObjectType.getObjCLifetime()) { + + if (DestructedType.getObjCLifetime() == Qualifiers::OCL_None) { + // Okay: just pretend that the user provided the correctly-qualified + // type. + } else { + Diag(DestructedTypeStart, diag::err_arc_pseudo_dtor_inconstant_quals) + << ObjectType << DestructedType << Base->getSourceRange() + << DestructedTypeInfo->getTypeLoc().getLocalSourceRange(); + } + + // Recover by setting the destructed type to the object type. + DestructedType = ObjectType; + DestructedTypeInfo = Context.getTrivialTypeSourceInfo(ObjectType, DestructedTypeStart); - Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo); + Destructed = PseudoDestructorTypeStorage(DestructedTypeInfo); + } } } @@ -4293,7 +4549,16 @@ ExprResult Sema::IgnoredValueConversions(Expr *E) { // [Except in specific positions,] an lvalue that does not have // array type is converted to the value stored in the // designated object (and is no longer an lvalue). - if (E->isRValue()) return Owned(E); + if (E->isRValue()) { + // In C, function designators (i.e. expressions of function type) + // are r-values, but we still want to do function-to-pointer decay + // on them. This is both technically correct and convenient for + // some clients. + if (!getLangOptions().CPlusPlus && E->getType()->isFunctionType()) + return DefaultFunctionArrayConversion(E); + + return Owned(E); + } // We always want to do this on ObjC property references. if (E->getObjectKind() == OK_ObjCProperty) { diff --git a/lib/Sema/SemaExprMember.cpp b/lib/Sema/SemaExprMember.cpp new file mode 100644 index 000000000000..2488dc8849dd --- /dev/null +++ b/lib/Sema/SemaExprMember.cpp @@ -0,0 +1,1594 @@ +//===--- SemaExprMember.cpp - Semantic Analysis for Expressions -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements semantic analysis member access expressions. +// +//===----------------------------------------------------------------------===// +#include "clang/Sema/SemaInternal.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Scope.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Lex/Preprocessor.h" + +using namespace clang; +using namespace sema; + +/// Determines if the given class is provably not derived from all of +/// the prospective base classes. +static bool IsProvablyNotDerivedFrom(Sema &SemaRef, + CXXRecordDecl *Record, + const llvm::SmallPtrSet<CXXRecordDecl*, 4> &Bases) { + if (Bases.count(Record->getCanonicalDecl())) + return false; + + RecordDecl *RD = Record->getDefinition(); + if (!RD) return false; + Record = cast<CXXRecordDecl>(RD); + + for (CXXRecordDecl::base_class_iterator I = Record->bases_begin(), + E = Record->bases_end(); I != E; ++I) { + CanQualType BaseT = SemaRef.Context.getCanonicalType((*I).getType()); + CanQual<RecordType> BaseRT = BaseT->getAs<RecordType>(); + if (!BaseRT) return false; + + CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl()); + if (!IsProvablyNotDerivedFrom(SemaRef, BaseRecord, Bases)) + return false; + } + + return true; +} + +enum IMAKind { + /// The reference is definitely not an instance member access. + IMA_Static, + + /// The reference may be an implicit instance member access. + IMA_Mixed, + + /// The reference may be to an instance member, but it is invalid if + /// so, because the context is not an instance method. + IMA_Mixed_StaticContext, + + /// The reference may be to an instance member, but it is invalid if + /// so, because the context is from an unrelated class. + IMA_Mixed_Unrelated, + + /// The reference is definitely an implicit instance member access. + IMA_Instance, + + /// The reference may be to an unresolved using declaration. + IMA_Unresolved, + + /// The reference may be to an unresolved using declaration and the + /// context is not an instance method. + IMA_Unresolved_StaticContext, + + /// All possible referrents are instance members and the current + /// context is not an instance method. + IMA_Error_StaticContext, + + /// All possible referrents are instance members of an unrelated + /// class. + IMA_Error_Unrelated +}; + +/// The given lookup names class member(s) and is not being used for +/// an address-of-member expression. Classify the type of access +/// according to whether it's possible that this reference names an +/// instance member. This is best-effort; it is okay to +/// conservatively answer "yes", in which case some errors will simply +/// not be caught until template-instantiation. +static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef, + Scope *CurScope, + const LookupResult &R) { + assert(!R.empty() && (*R.begin())->isCXXClassMember()); + + DeclContext *DC = SemaRef.getFunctionLevelDeclContext(); + + bool isStaticContext = + (!isa<CXXMethodDecl>(DC) || + cast<CXXMethodDecl>(DC)->isStatic()); + + // C++0x [expr.prim]p4: + // Otherwise, if a member-declarator declares a non-static data member + // of a class X, the expression this is a prvalue of type "pointer to X" + // within the optional brace-or-equal-initializer. + if (CurScope->getFlags() & Scope::ThisScope) + isStaticContext = false; + + if (R.isUnresolvableResult()) + return isStaticContext ? IMA_Unresolved_StaticContext : IMA_Unresolved; + + // Collect all the declaring classes of instance members we find. + bool hasNonInstance = false; + bool hasField = false; + llvm::SmallPtrSet<CXXRecordDecl*, 4> Classes; + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + NamedDecl *D = *I; + + if (D->isCXXInstanceMember()) { + if (dyn_cast<FieldDecl>(D)) + hasField = true; + + CXXRecordDecl *R = cast<CXXRecordDecl>(D->getDeclContext()); + Classes.insert(R->getCanonicalDecl()); + } + else + hasNonInstance = true; + } + + // If we didn't find any instance members, it can't be an implicit + // member reference. + if (Classes.empty()) + return IMA_Static; + + // If the current context is not an instance method, it can't be + // an implicit member reference. + if (isStaticContext) { + if (hasNonInstance) + return IMA_Mixed_StaticContext; + + if (SemaRef.getLangOptions().CPlusPlus0x && hasField) { + // C++0x [expr.prim.general]p10: + // An id-expression that denotes a non-static data member or non-static + // member function of a class can only be used: + // (...) + // - if that id-expression denotes a non-static data member and it + // appears in an unevaluated operand. + const Sema::ExpressionEvaluationContextRecord& record + = SemaRef.ExprEvalContexts.back(); + bool isUnevaluatedExpression = (record.Context == Sema::Unevaluated); + if (isUnevaluatedExpression) + return IMA_Mixed_StaticContext; + } + + return IMA_Error_StaticContext; + } + + CXXRecordDecl *contextClass; + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(DC)) + contextClass = MD->getParent()->getCanonicalDecl(); + else + contextClass = cast<CXXRecordDecl>(DC); + + // [class.mfct.non-static]p3: + // ...is used in the body of a non-static member function of class X, + // if name lookup (3.4.1) resolves the name in the id-expression to a + // non-static non-type member of some class C [...] + // ...if C is not X or a base class of X, the class member access expression + // is ill-formed. + if (R.getNamingClass() && + contextClass != R.getNamingClass()->getCanonicalDecl() && + contextClass->isProvablyNotDerivedFrom(R.getNamingClass())) + return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); + + // If we can prove that the current context is unrelated to all the + // declaring classes, it can't be an implicit member reference (in + // which case it's an error if any of those members are selected). + if (IsProvablyNotDerivedFrom(SemaRef, contextClass, Classes)) + return (hasNonInstance ? IMA_Mixed_Unrelated : IMA_Error_Unrelated); + + return (hasNonInstance ? IMA_Mixed : IMA_Instance); +} + +/// Diagnose a reference to a field with no object available. +static void DiagnoseInstanceReference(Sema &SemaRef, + const CXXScopeSpec &SS, + NamedDecl *rep, + const DeclarationNameInfo &nameInfo) { + SourceLocation Loc = nameInfo.getLoc(); + SourceRange Range(Loc); + if (SS.isSet()) Range.setBegin(SS.getRange().getBegin()); + + if (isa<FieldDecl>(rep) || isa<IndirectFieldDecl>(rep)) { + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(SemaRef.CurContext)) { + if (MD->isStatic()) { + // "invalid use of member 'x' in static member function" + SemaRef.Diag(Loc, diag::err_invalid_member_use_in_static_method) + << Range << nameInfo.getName(); + return; + } + } + + SemaRef.Diag(Loc, diag::err_invalid_non_static_member_use) + << nameInfo.getName() << Range; + return; + } + + SemaRef.Diag(Loc, diag::err_member_call_without_object) << Range; +} + +/// Builds an expression which might be an implicit member expression. +ExprResult +Sema::BuildPossibleImplicitMemberExpr(const CXXScopeSpec &SS, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs) { + switch (ClassifyImplicitMemberAccess(*this, CurScope, R)) { + case IMA_Instance: + return BuildImplicitMemberExpr(SS, R, TemplateArgs, true); + + case IMA_Mixed: + case IMA_Mixed_Unrelated: + case IMA_Unresolved: + return BuildImplicitMemberExpr(SS, R, TemplateArgs, false); + + case IMA_Static: + case IMA_Mixed_StaticContext: + case IMA_Unresolved_StaticContext: + if (TemplateArgs) + return BuildTemplateIdExpr(SS, R, false, *TemplateArgs); + return BuildDeclarationNameExpr(SS, R, false); + + case IMA_Error_StaticContext: + case IMA_Error_Unrelated: + DiagnoseInstanceReference(*this, SS, R.getRepresentativeDecl(), + R.getLookupNameInfo()); + return ExprError(); + } + + llvm_unreachable("unexpected instance member access kind"); + return ExprError(); +} + +/// Check an ext-vector component access expression. +/// +/// VK should be set in advance to the value kind of the base +/// expression. +static QualType +CheckExtVectorComponent(Sema &S, QualType baseType, ExprValueKind &VK, + SourceLocation OpLoc, const IdentifierInfo *CompName, + SourceLocation CompLoc) { + // FIXME: Share logic with ExtVectorElementExpr::containsDuplicateElements, + // see FIXME there. + // + // FIXME: This logic can be greatly simplified by splitting it along + // halving/not halving and reworking the component checking. + const ExtVectorType *vecType = baseType->getAs<ExtVectorType>(); + + // The vector accessor can't exceed the number of elements. + const char *compStr = CompName->getNameStart(); + + // This flag determines whether or not the component is one of the four + // special names that indicate a subset of exactly half the elements are + // to be selected. + bool HalvingSwizzle = false; + + // This flag determines whether or not CompName has an 's' char prefix, + // indicating that it is a string of hex values to be used as vector indices. + bool HexSwizzle = *compStr == 's' || *compStr == 'S'; + + bool HasRepeated = false; + bool HasIndex[16] = {}; + + int Idx; + + // Check that we've found one of the special components, or that the component + // names must come from the same set. + if (!strcmp(compStr, "hi") || !strcmp(compStr, "lo") || + !strcmp(compStr, "even") || !strcmp(compStr, "odd")) { + HalvingSwizzle = true; + } else if (!HexSwizzle && + (Idx = vecType->getPointAccessorIdx(*compStr)) != -1) { + do { + if (HasIndex[Idx]) HasRepeated = true; + HasIndex[Idx] = true; + compStr++; + } while (*compStr && (Idx = vecType->getPointAccessorIdx(*compStr)) != -1); + } else { + if (HexSwizzle) compStr++; + while ((Idx = vecType->getNumericAccessorIdx(*compStr)) != -1) { + if (HasIndex[Idx]) HasRepeated = true; + HasIndex[Idx] = true; + compStr++; + } + } + + if (!HalvingSwizzle && *compStr) { + // We didn't get to the end of the string. This means the component names + // didn't come from the same set *or* we encountered an illegal name. + S.Diag(OpLoc, diag::err_ext_vector_component_name_illegal) + << llvm::StringRef(compStr, 1) << SourceRange(CompLoc); + return QualType(); + } + + // Ensure no component accessor exceeds the width of the vector type it + // operates on. + if (!HalvingSwizzle) { + compStr = CompName->getNameStart(); + + if (HexSwizzle) + compStr++; + + while (*compStr) { + if (!vecType->isAccessorWithinNumElements(*compStr++)) { + S.Diag(OpLoc, diag::err_ext_vector_component_exceeds_length) + << baseType << SourceRange(CompLoc); + return QualType(); + } + } + } + + // The component accessor looks fine - now we need to compute the actual type. + // The vector type is implied by the component accessor. For example, + // vec4.b is a float, vec4.xy is a vec2, vec4.rgb is a vec3, etc. + // vec4.s0 is a float, vec4.s23 is a vec3, etc. + // vec4.hi, vec4.lo, vec4.e, and vec4.o all return vec2. + unsigned CompSize = HalvingSwizzle ? (vecType->getNumElements() + 1) / 2 + : CompName->getLength(); + if (HexSwizzle) + CompSize--; + + if (CompSize == 1) + return vecType->getElementType(); + + if (HasRepeated) VK = VK_RValue; + + QualType VT = S.Context.getExtVectorType(vecType->getElementType(), CompSize); + // Now look up the TypeDefDecl from the vector type. Without this, + // diagostics look bad. We want extended vector types to appear built-in. + for (unsigned i = 0, E = S.ExtVectorDecls.size(); i != E; ++i) { + if (S.ExtVectorDecls[i]->getUnderlyingType() == VT) + return S.Context.getTypedefType(S.ExtVectorDecls[i]); + } + return VT; // should never get here (a typedef type should always be found). +} + +static Decl *FindGetterSetterNameDeclFromProtocolList(const ObjCProtocolDecl*PDecl, + IdentifierInfo *Member, + const Selector &Sel, + ASTContext &Context) { + if (Member) + if (ObjCPropertyDecl *PD = PDecl->FindPropertyDeclaration(Member)) + return PD; + if (ObjCMethodDecl *OMD = PDecl->getInstanceMethod(Sel)) + return OMD; + + for (ObjCProtocolDecl::protocol_iterator I = PDecl->protocol_begin(), + E = PDecl->protocol_end(); I != E; ++I) { + if (Decl *D = FindGetterSetterNameDeclFromProtocolList(*I, Member, Sel, + Context)) + return D; + } + return 0; +} + +static Decl *FindGetterSetterNameDecl(const ObjCObjectPointerType *QIdTy, + IdentifierInfo *Member, + const Selector &Sel, + ASTContext &Context) { + // Check protocols on qualified interfaces. + Decl *GDecl = 0; + for (ObjCObjectPointerType::qual_iterator I = QIdTy->qual_begin(), + E = QIdTy->qual_end(); I != E; ++I) { + if (Member) + if (ObjCPropertyDecl *PD = (*I)->FindPropertyDeclaration(Member)) { + GDecl = PD; + break; + } + // Also must look for a getter or setter name which uses property syntax. + if (ObjCMethodDecl *OMD = (*I)->getInstanceMethod(Sel)) { + GDecl = OMD; + break; + } + } + if (!GDecl) { + for (ObjCObjectPointerType::qual_iterator I = QIdTy->qual_begin(), + E = QIdTy->qual_end(); I != E; ++I) { + // Search in the protocol-qualifier list of current protocol. + GDecl = FindGetterSetterNameDeclFromProtocolList(*I, Member, Sel, + Context); + if (GDecl) + return GDecl; + } + } + return GDecl; +} + +ExprResult +Sema::ActOnDependentMemberExpr(Expr *BaseExpr, QualType BaseType, + bool IsArrow, SourceLocation OpLoc, + const CXXScopeSpec &SS, + NamedDecl *FirstQualifierInScope, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs) { + // Even in dependent contexts, try to diagnose base expressions with + // obviously wrong types, e.g.: + // + // T* t; + // t.f; + // + // In Obj-C++, however, the above expression is valid, since it could be + // accessing the 'f' property if T is an Obj-C interface. The extra check + // allows this, while still reporting an error if T is a struct pointer. + if (!IsArrow) { + const PointerType *PT = BaseType->getAs<PointerType>(); + if (PT && (!getLangOptions().ObjC1 || + PT->getPointeeType()->isRecordType())) { + assert(BaseExpr && "cannot happen with implicit member accesses"); + Diag(NameInfo.getLoc(), diag::err_typecheck_member_reference_struct_union) + << BaseType << BaseExpr->getSourceRange(); + return ExprError(); + } + } + + assert(BaseType->isDependentType() || + NameInfo.getName().isDependentName() || + isDependentScopeSpecifier(SS)); + + // Get the type being accessed in BaseType. If this is an arrow, the BaseExpr + // must have pointer type, and the accessed type is the pointee. + return Owned(CXXDependentScopeMemberExpr::Create(Context, BaseExpr, BaseType, + IsArrow, OpLoc, + SS.getWithLocInContext(Context), + FirstQualifierInScope, + NameInfo, TemplateArgs)); +} + +/// We know that the given qualified member reference points only to +/// declarations which do not belong to the static type of the base +/// expression. Diagnose the problem. +static void DiagnoseQualifiedMemberReference(Sema &SemaRef, + Expr *BaseExpr, + QualType BaseType, + const CXXScopeSpec &SS, + NamedDecl *rep, + const DeclarationNameInfo &nameInfo) { + // If this is an implicit member access, use a different set of + // diagnostics. + if (!BaseExpr) + return DiagnoseInstanceReference(SemaRef, SS, rep, nameInfo); + + SemaRef.Diag(nameInfo.getLoc(), diag::err_qualified_member_of_unrelated) + << SS.getRange() << rep << BaseType; +} + +// Check whether the declarations we found through a nested-name +// specifier in a member expression are actually members of the base +// type. The restriction here is: +// +// C++ [expr.ref]p2: +// ... In these cases, the id-expression shall name a +// member of the class or of one of its base classes. +// +// So it's perfectly legitimate for the nested-name specifier to name +// an unrelated class, and for us to find an overload set including +// decls from classes which are not superclasses, as long as the decl +// we actually pick through overload resolution is from a superclass. +bool Sema::CheckQualifiedMemberReference(Expr *BaseExpr, + QualType BaseType, + const CXXScopeSpec &SS, + const LookupResult &R) { + const RecordType *BaseRT = BaseType->getAs<RecordType>(); + if (!BaseRT) { + // We can't check this yet because the base type is still + // dependent. + assert(BaseType->isDependentType()); + return false; + } + CXXRecordDecl *BaseRecord = cast<CXXRecordDecl>(BaseRT->getDecl()); + + for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) { + // If this is an implicit member reference and we find a + // non-instance member, it's not an error. + if (!BaseExpr && !(*I)->isCXXInstanceMember()) + return false; + + // Note that we use the DC of the decl, not the underlying decl. + DeclContext *DC = (*I)->getDeclContext(); + while (DC->isTransparentContext()) + DC = DC->getParent(); + + if (!DC->isRecord()) + continue; + + llvm::SmallPtrSet<CXXRecordDecl*,4> MemberRecord; + MemberRecord.insert(cast<CXXRecordDecl>(DC)->getCanonicalDecl()); + + if (!IsProvablyNotDerivedFrom(*this, BaseRecord, MemberRecord)) + return false; + } + + DiagnoseQualifiedMemberReference(*this, BaseExpr, BaseType, SS, + R.getRepresentativeDecl(), + R.getLookupNameInfo()); + return true; +} + +static bool +LookupMemberExprInRecord(Sema &SemaRef, LookupResult &R, + SourceRange BaseRange, const RecordType *RTy, + SourceLocation OpLoc, CXXScopeSpec &SS, + bool HasTemplateArgs) { + RecordDecl *RDecl = RTy->getDecl(); + if (SemaRef.RequireCompleteType(OpLoc, QualType(RTy, 0), + SemaRef.PDiag(diag::err_typecheck_incomplete_tag) + << BaseRange)) + return true; + + if (HasTemplateArgs) { + // LookupTemplateName doesn't expect these both to exist simultaneously. + QualType ObjectType = SS.isSet() ? QualType() : QualType(RTy, 0); + + bool MOUS; + SemaRef.LookupTemplateName(R, 0, SS, ObjectType, false, MOUS); + return false; + } + + DeclContext *DC = RDecl; + if (SS.isSet()) { + // If the member name was a qualified-id, look into the + // nested-name-specifier. + DC = SemaRef.computeDeclContext(SS, false); + + if (SemaRef.RequireCompleteDeclContext(SS, DC)) { + SemaRef.Diag(SS.getRange().getEnd(), diag::err_typecheck_incomplete_tag) + << SS.getRange() << DC; + return true; + } + + assert(DC && "Cannot handle non-computable dependent contexts in lookup"); + + if (!isa<TypeDecl>(DC)) { + SemaRef.Diag(R.getNameLoc(), diag::err_qualified_member_nonclass) + << DC << SS.getRange(); + return true; + } + } + + // The record definition is complete, now look up the member. + SemaRef.LookupQualifiedName(R, DC); + + if (!R.empty()) + return false; + + // We didn't find anything with the given name, so try to correct + // for typos. + DeclarationName Name = R.getLookupName(); + TypoCorrection Corrected = SemaRef.CorrectTypo(R.getLookupNameInfo(), + R.getLookupKind(), NULL, + &SS, DC, false, + Sema::CTC_MemberLookup); + NamedDecl *ND = Corrected.getCorrectionDecl(); + R.clear(); + if (ND && (isa<ValueDecl>(ND) || isa<FunctionTemplateDecl>(ND))) { + std::string CorrectedStr( + Corrected.getAsString(SemaRef.getLangOptions())); + std::string CorrectedQuotedStr( + Corrected.getQuoted(SemaRef.getLangOptions())); + R.setLookupName(Corrected.getCorrection()); + R.addDecl(ND); + SemaRef.Diag(R.getNameLoc(), diag::err_no_member_suggest) + << Name << DC << CorrectedQuotedStr << SS.getRange() + << FixItHint::CreateReplacement(R.getNameLoc(), CorrectedStr); + SemaRef.Diag(ND->getLocation(), diag::note_previous_decl) + << ND->getDeclName(); + } + + return false; +} + +ExprResult +Sema::BuildMemberReferenceExpr(Expr *Base, QualType BaseType, + SourceLocation OpLoc, bool IsArrow, + CXXScopeSpec &SS, + NamedDecl *FirstQualifierInScope, + const DeclarationNameInfo &NameInfo, + const TemplateArgumentListInfo *TemplateArgs) { + if (BaseType->isDependentType() || + (SS.isSet() && isDependentScopeSpecifier(SS))) + return ActOnDependentMemberExpr(Base, BaseType, + IsArrow, OpLoc, + SS, FirstQualifierInScope, + NameInfo, TemplateArgs); + + LookupResult R(*this, NameInfo, LookupMemberName); + + // Implicit member accesses. + if (!Base) { + QualType RecordTy = BaseType; + if (IsArrow) RecordTy = RecordTy->getAs<PointerType>()->getPointeeType(); + if (LookupMemberExprInRecord(*this, R, SourceRange(), + RecordTy->getAs<RecordType>(), + OpLoc, SS, TemplateArgs != 0)) + return ExprError(); + + // Explicit member accesses. + } else { + ExprResult BaseResult = Owned(Base); + ExprResult Result = + LookupMemberExpr(R, BaseResult, IsArrow, OpLoc, + SS, /*ObjCImpDecl*/ 0, TemplateArgs != 0); + + if (BaseResult.isInvalid()) + return ExprError(); + Base = BaseResult.take(); + + if (Result.isInvalid()) { + Owned(Base); + return ExprError(); + } + + if (Result.get()) + return move(Result); + + // LookupMemberExpr can modify Base, and thus change BaseType + BaseType = Base->getType(); + } + + return BuildMemberReferenceExpr(Base, BaseType, + OpLoc, IsArrow, SS, FirstQualifierInScope, + R, TemplateArgs); +} + +static ExprResult +BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow, + const CXXScopeSpec &SS, FieldDecl *Field, + DeclAccessPair FoundDecl, + const DeclarationNameInfo &MemberNameInfo); + +ExprResult +Sema::BuildAnonymousStructUnionMemberReference(const CXXScopeSpec &SS, + SourceLocation loc, + IndirectFieldDecl *indirectField, + Expr *baseObjectExpr, + SourceLocation opLoc) { + // First, build the expression that refers to the base object. + + bool baseObjectIsPointer = false; + Qualifiers baseQuals; + + // Case 1: the base of the indirect field is not a field. + VarDecl *baseVariable = indirectField->getVarDecl(); + CXXScopeSpec EmptySS; + if (baseVariable) { + assert(baseVariable->getType()->isRecordType()); + + // In principle we could have a member access expression that + // accesses an anonymous struct/union that's a static member of + // the base object's class. However, under the current standard, + // static data members cannot be anonymous structs or unions. + // Supporting this is as easy as building a MemberExpr here. + assert(!baseObjectExpr && "anonymous struct/union is static data member?"); + + DeclarationNameInfo baseNameInfo(DeclarationName(), loc); + + ExprResult result + = BuildDeclarationNameExpr(EmptySS, baseNameInfo, baseVariable); + if (result.isInvalid()) return ExprError(); + + baseObjectExpr = result.take(); + baseObjectIsPointer = false; + baseQuals = baseObjectExpr->getType().getQualifiers(); + + // Case 2: the base of the indirect field is a field and the user + // wrote a member expression. + } else if (baseObjectExpr) { + // The caller provided the base object expression. Determine + // whether its a pointer and whether it adds any qualifiers to the + // anonymous struct/union fields we're looking into. + QualType objectType = baseObjectExpr->getType(); + + if (const PointerType *ptr = objectType->getAs<PointerType>()) { + baseObjectIsPointer = true; + objectType = ptr->getPointeeType(); + } else { + baseObjectIsPointer = false; + } + baseQuals = objectType.getQualifiers(); + + // Case 3: the base of the indirect field is a field and we should + // build an implicit member access. + } else { + // We've found a member of an anonymous struct/union that is + // inside a non-anonymous struct/union, so in a well-formed + // program our base object expression is "this". + QualType ThisTy = getAndCaptureCurrentThisType(); + if (ThisTy.isNull()) { + Diag(loc, diag::err_invalid_member_use_in_static_method) + << indirectField->getDeclName(); + return ExprError(); + } + + // Our base object expression is "this". + baseObjectExpr + = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/ true); + baseObjectIsPointer = true; + baseQuals = ThisTy->castAs<PointerType>()->getPointeeType().getQualifiers(); + } + + // Build the implicit member references to the field of the + // anonymous struct/union. + Expr *result = baseObjectExpr; + IndirectFieldDecl::chain_iterator + FI = indirectField->chain_begin(), FEnd = indirectField->chain_end(); + + // Build the first member access in the chain with full information. + if (!baseVariable) { + FieldDecl *field = cast<FieldDecl>(*FI); + + // FIXME: use the real found-decl info! + DeclAccessPair foundDecl = DeclAccessPair::make(field, field->getAccess()); + + // Make a nameInfo that properly uses the anonymous name. + DeclarationNameInfo memberNameInfo(field->getDeclName(), loc); + + result = BuildFieldReferenceExpr(*this, result, baseObjectIsPointer, + EmptySS, field, foundDecl, + memberNameInfo).take(); + baseObjectIsPointer = false; + + // FIXME: check qualified member access + } + + // In all cases, we should now skip the first declaration in the chain. + ++FI; + + while (FI != FEnd) { + FieldDecl *field = cast<FieldDecl>(*FI++); + + // FIXME: these are somewhat meaningless + DeclarationNameInfo memberNameInfo(field->getDeclName(), loc); + DeclAccessPair foundDecl = DeclAccessPair::make(field, field->getAccess()); + + result = BuildFieldReferenceExpr(*this, result, /*isarrow*/ false, + (FI == FEnd? SS : EmptySS), field, + foundDecl, memberNameInfo).take(); + } + + return Owned(result); +} + +/// \brief Build a MemberExpr AST node. +static MemberExpr *BuildMemberExpr(ASTContext &C, Expr *Base, bool isArrow, + const CXXScopeSpec &SS, ValueDecl *Member, + DeclAccessPair FoundDecl, + const DeclarationNameInfo &MemberNameInfo, + QualType Ty, + ExprValueKind VK, ExprObjectKind OK, + const TemplateArgumentListInfo *TemplateArgs = 0) { + return MemberExpr::Create(C, Base, isArrow, SS.getWithLocInContext(C), + Member, FoundDecl, MemberNameInfo, + TemplateArgs, Ty, VK, OK); +} + +ExprResult +Sema::BuildMemberReferenceExpr(Expr *BaseExpr, QualType BaseExprType, + SourceLocation OpLoc, bool IsArrow, + const CXXScopeSpec &SS, + NamedDecl *FirstQualifierInScope, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs, + bool SuppressQualifierCheck) { + QualType BaseType = BaseExprType; + if (IsArrow) { + assert(BaseType->isPointerType()); + BaseType = BaseType->getAs<PointerType>()->getPointeeType(); + } + R.setBaseObjectType(BaseType); + + const DeclarationNameInfo &MemberNameInfo = R.getLookupNameInfo(); + DeclarationName MemberName = MemberNameInfo.getName(); + SourceLocation MemberLoc = MemberNameInfo.getLoc(); + + if (R.isAmbiguous()) + return ExprError(); + + if (R.empty()) { + // Rederive where we looked up. + DeclContext *DC = (SS.isSet() + ? computeDeclContext(SS, false) + : BaseType->getAs<RecordType>()->getDecl()); + + Diag(R.getNameLoc(), diag::err_no_member) + << MemberName << DC + << (BaseExpr ? BaseExpr->getSourceRange() : SourceRange()); + return ExprError(); + } + + // Diagnose lookups that find only declarations from a non-base + // type. This is possible for either qualified lookups (which may + // have been qualified with an unrelated type) or implicit member + // expressions (which were found with unqualified lookup and thus + // may have come from an enclosing scope). Note that it's okay for + // lookup to find declarations from a non-base type as long as those + // aren't the ones picked by overload resolution. + if ((SS.isSet() || !BaseExpr || + (isa<CXXThisExpr>(BaseExpr) && + cast<CXXThisExpr>(BaseExpr)->isImplicit())) && + !SuppressQualifierCheck && + CheckQualifiedMemberReference(BaseExpr, BaseType, SS, R)) + return ExprError(); + + // Construct an unresolved result if we in fact got an unresolved + // result. + if (R.isOverloadedResult() || R.isUnresolvableResult()) { + // Suppress any lookup-related diagnostics; we'll do these when we + // pick a member. + R.suppressDiagnostics(); + + UnresolvedMemberExpr *MemExpr + = UnresolvedMemberExpr::Create(Context, R.isUnresolvableResult(), + BaseExpr, BaseExprType, + IsArrow, OpLoc, + SS.getWithLocInContext(Context), + MemberNameInfo, + TemplateArgs, R.begin(), R.end()); + + return Owned(MemExpr); + } + + assert(R.isSingleResult()); + DeclAccessPair FoundDecl = R.begin().getPair(); + NamedDecl *MemberDecl = R.getFoundDecl(); + + // FIXME: diagnose the presence of template arguments now. + + // If the decl being referenced had an error, return an error for this + // sub-expr without emitting another error, in order to avoid cascading + // error cases. + if (MemberDecl->isInvalidDecl()) + return ExprError(); + + // Handle the implicit-member-access case. + if (!BaseExpr) { + // If this is not an instance member, convert to a non-member access. + if (!MemberDecl->isCXXInstanceMember()) + return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), MemberDecl); + + SourceLocation Loc = R.getNameLoc(); + if (SS.getRange().isValid()) + Loc = SS.getRange().getBegin(); + BaseExpr = new (Context) CXXThisExpr(Loc, BaseExprType,/*isImplicit=*/true); + } + + bool ShouldCheckUse = true; + if (CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(MemberDecl)) { + // Don't diagnose the use of a virtual member function unless it's + // explicitly qualified. + if (MD->isVirtual() && !SS.isSet()) + ShouldCheckUse = false; + } + + // Check the use of this member. + if (ShouldCheckUse && DiagnoseUseOfDecl(MemberDecl, MemberLoc)) { + Owned(BaseExpr); + return ExprError(); + } + + // Perform a property load on the base regardless of whether we + // actually need it for the declaration. + if (BaseExpr->getObjectKind() == OK_ObjCProperty) { + ExprResult Result = ConvertPropertyForRValue(BaseExpr); + if (Result.isInvalid()) + return ExprError(); + BaseExpr = Result.take(); + } + + if (FieldDecl *FD = dyn_cast<FieldDecl>(MemberDecl)) + return BuildFieldReferenceExpr(*this, BaseExpr, IsArrow, + SS, FD, FoundDecl, MemberNameInfo); + + if (IndirectFieldDecl *FD = dyn_cast<IndirectFieldDecl>(MemberDecl)) + // We may have found a field within an anonymous union or struct + // (C++ [class.union]). + return BuildAnonymousStructUnionMemberReference(SS, MemberLoc, FD, + BaseExpr, OpLoc); + + if (VarDecl *Var = dyn_cast<VarDecl>(MemberDecl)) { + MarkDeclarationReferenced(MemberLoc, Var); + return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, + Var, FoundDecl, MemberNameInfo, + Var->getType().getNonReferenceType(), + VK_LValue, OK_Ordinary)); + } + + if (CXXMethodDecl *MemberFn = dyn_cast<CXXMethodDecl>(MemberDecl)) { + ExprValueKind valueKind; + QualType type; + if (MemberFn->isInstance()) { + valueKind = VK_RValue; + type = Context.BoundMemberTy; + } else { + valueKind = VK_LValue; + type = MemberFn->getType(); + } + + MarkDeclarationReferenced(MemberLoc, MemberDecl); + return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, + MemberFn, FoundDecl, MemberNameInfo, + type, valueKind, OK_Ordinary)); + } + assert(!isa<FunctionDecl>(MemberDecl) && "member function not C++ method?"); + + if (EnumConstantDecl *Enum = dyn_cast<EnumConstantDecl>(MemberDecl)) { + MarkDeclarationReferenced(MemberLoc, MemberDecl); + return Owned(BuildMemberExpr(Context, BaseExpr, IsArrow, SS, + Enum, FoundDecl, MemberNameInfo, + Enum->getType(), VK_RValue, OK_Ordinary)); + } + + Owned(BaseExpr); + + // We found something that we didn't expect. Complain. + if (isa<TypeDecl>(MemberDecl)) + Diag(MemberLoc, diag::err_typecheck_member_reference_type) + << MemberName << BaseType << int(IsArrow); + else + Diag(MemberLoc, diag::err_typecheck_member_reference_unknown) + << MemberName << BaseType << int(IsArrow); + + Diag(MemberDecl->getLocation(), diag::note_member_declared_here) + << MemberName; + R.suppressDiagnostics(); + return ExprError(); +} + +/// Given that normal member access failed on the given expression, +/// and given that the expression's type involves builtin-id or +/// builtin-Class, decide whether substituting in the redefinition +/// types would be profitable. The redefinition type is whatever +/// this translation unit tried to typedef to id/Class; we store +/// it to the side and then re-use it in places like this. +static bool ShouldTryAgainWithRedefinitionType(Sema &S, ExprResult &base) { + const ObjCObjectPointerType *opty + = base.get()->getType()->getAs<ObjCObjectPointerType>(); + if (!opty) return false; + + const ObjCObjectType *ty = opty->getObjectType(); + + QualType redef; + if (ty->isObjCId()) { + redef = S.Context.ObjCIdRedefinitionType; + } else if (ty->isObjCClass()) { + redef = S.Context.ObjCClassRedefinitionType; + } else { + return false; + } + + // Do the substitution as long as the redefinition type isn't just a + // possibly-qualified pointer to builtin-id or builtin-Class again. + opty = redef->getAs<ObjCObjectPointerType>(); + if (opty && !opty->getObjectType()->getInterface() != 0) + return false; + + base = S.ImpCastExprToType(base.take(), redef, CK_BitCast); + return true; +} + +/// Look up the given member of the given non-type-dependent +/// expression. This can return in one of two ways: +/// * If it returns a sentinel null-but-valid result, the caller will +/// assume that lookup was performed and the results written into +/// the provided structure. It will take over from there. +/// * Otherwise, the returned expression will be produced in place of +/// an ordinary member expression. +/// +/// The ObjCImpDecl bit is a gross hack that will need to be properly +/// fixed for ObjC++. +ExprResult +Sema::LookupMemberExpr(LookupResult &R, ExprResult &BaseExpr, + bool &IsArrow, SourceLocation OpLoc, + CXXScopeSpec &SS, + Decl *ObjCImpDecl, bool HasTemplateArgs) { + assert(BaseExpr.get() && "no base expression"); + + // Perform default conversions. + BaseExpr = DefaultFunctionArrayConversion(BaseExpr.take()); + + if (IsArrow) { + BaseExpr = DefaultLvalueConversion(BaseExpr.take()); + if (BaseExpr.isInvalid()) + return ExprError(); + } + + QualType BaseType = BaseExpr.get()->getType(); + assert(!BaseType->isDependentType()); + + DeclarationName MemberName = R.getLookupName(); + SourceLocation MemberLoc = R.getNameLoc(); + + // For later type-checking purposes, turn arrow accesses into dot + // accesses. The only access type we support that doesn't follow + // the C equivalence "a->b === (*a).b" is ObjC property accesses, + // and those never use arrows, so this is unaffected. + if (IsArrow) { + if (const PointerType *Ptr = BaseType->getAs<PointerType>()) + BaseType = Ptr->getPointeeType(); + else if (const ObjCObjectPointerType *Ptr + = BaseType->getAs<ObjCObjectPointerType>()) + BaseType = Ptr->getPointeeType(); + else if (BaseType->isRecordType()) { + // Recover from arrow accesses to records, e.g.: + // struct MyRecord foo; + // foo->bar + // This is actually well-formed in C++ if MyRecord has an + // overloaded operator->, but that should have been dealt with + // by now. + Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "."); + IsArrow = false; + } else if (BaseType == Context.BoundMemberTy) { + goto fail; + } else { + Diag(MemberLoc, diag::err_typecheck_member_reference_arrow) + << BaseType << BaseExpr.get()->getSourceRange(); + return ExprError(); + } + } + + // Handle field access to simple records. + if (const RecordType *RTy = BaseType->getAs<RecordType>()) { + if (LookupMemberExprInRecord(*this, R, BaseExpr.get()->getSourceRange(), + RTy, OpLoc, SS, HasTemplateArgs)) + return ExprError(); + + // Returning valid-but-null is how we indicate to the caller that + // the lookup result was filled in. + return Owned((Expr*) 0); + } + + // Handle ivar access to Objective-C objects. + if (const ObjCObjectType *OTy = BaseType->getAs<ObjCObjectType>()) { + IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); + + // There are three cases for the base type: + // - builtin id (qualified or unqualified) + // - builtin Class (qualified or unqualified) + // - an interface + ObjCInterfaceDecl *IDecl = OTy->getInterface(); + if (!IDecl) { + if (getLangOptions().ObjCAutoRefCount && + (OTy->isObjCId() || OTy->isObjCClass())) + goto fail; + // There's an implicit 'isa' ivar on all objects. + // But we only actually find it this way on objects of type 'id', + // apparently. + if (OTy->isObjCId() && Member->isStr("isa")) + return Owned(new (Context) ObjCIsaExpr(BaseExpr.take(), IsArrow, MemberLoc, + Context.getObjCClassType())); + + if (ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + goto fail; + } + + ObjCInterfaceDecl *ClassDeclared; + ObjCIvarDecl *IV = IDecl->lookupInstanceVariable(Member, ClassDeclared); + + if (!IV) { + // Attempt to correct for typos in ivar names. + LookupResult Res(*this, R.getLookupName(), R.getNameLoc(), + LookupMemberName); + TypoCorrection Corrected = CorrectTypo( + R.getLookupNameInfo(), LookupMemberName, NULL, NULL, IDecl, false, + IsArrow ? CTC_ObjCIvarLookup : CTC_ObjCPropertyLookup); + if ((IV = Corrected.getCorrectionDeclAs<ObjCIvarDecl>())) { + Diag(R.getNameLoc(), + diag::err_typecheck_member_reference_ivar_suggest) + << IDecl->getDeclName() << MemberName << IV->getDeclName() + << FixItHint::CreateReplacement(R.getNameLoc(), + IV->getNameAsString()); + Diag(IV->getLocation(), diag::note_previous_decl) + << IV->getDeclName(); + } else { + if (IsArrow && IDecl->FindPropertyDeclaration(Member)) { + Diag(MemberLoc, + diag::err_property_found_suggest) + << Member << BaseExpr.get()->getType() + << FixItHint::CreateReplacement(OpLoc, "."); + return ExprError(); + } + Res.clear(); + Res.setLookupName(Member); + + Diag(MemberLoc, diag::err_typecheck_member_reference_ivar) + << IDecl->getDeclName() << MemberName + << BaseExpr.get()->getSourceRange(); + return ExprError(); + } + } + + // If the decl being referenced had an error, return an error for this + // sub-expr without emitting another error, in order to avoid cascading + // error cases. + if (IV->isInvalidDecl()) + return ExprError(); + + // Check whether we can reference this field. + if (DiagnoseUseOfDecl(IV, MemberLoc)) + return ExprError(); + if (IV->getAccessControl() != ObjCIvarDecl::Public && + IV->getAccessControl() != ObjCIvarDecl::Package) { + ObjCInterfaceDecl *ClassOfMethodDecl = 0; + if (ObjCMethodDecl *MD = getCurMethodDecl()) + ClassOfMethodDecl = MD->getClassInterface(); + else if (ObjCImpDecl && getCurFunctionDecl()) { + // Case of a c-function declared inside an objc implementation. + // FIXME: For a c-style function nested inside an objc implementation + // class, there is no implementation context available, so we pass + // down the context as argument to this routine. Ideally, this context + // need be passed down in the AST node and somehow calculated from the + // AST for a function decl. + if (ObjCImplementationDecl *IMPD = + dyn_cast<ObjCImplementationDecl>(ObjCImpDecl)) + ClassOfMethodDecl = IMPD->getClassInterface(); + else if (ObjCCategoryImplDecl* CatImplClass = + dyn_cast<ObjCCategoryImplDecl>(ObjCImpDecl)) + ClassOfMethodDecl = CatImplClass->getClassInterface(); + } + + if (IV->getAccessControl() == ObjCIvarDecl::Private) { + if (ClassDeclared != IDecl || + ClassOfMethodDecl != ClassDeclared) + Diag(MemberLoc, diag::error_private_ivar_access) + << IV->getDeclName(); + } else if (!IDecl->isSuperClassOf(ClassOfMethodDecl)) + // @protected + Diag(MemberLoc, diag::error_protected_ivar_access) + << IV->getDeclName(); + } + if (getLangOptions().ObjCAutoRefCount) { + Expr *BaseExp = BaseExpr.get()->IgnoreParenImpCasts(); + if (UnaryOperator *UO = dyn_cast<UnaryOperator>(BaseExp)) + if (UO->getOpcode() == UO_Deref) + BaseExp = UO->getSubExpr()->IgnoreParenCasts(); + + if (DeclRefExpr *DE = dyn_cast<DeclRefExpr>(BaseExp)) + if (DE->getType().getObjCLifetime() == Qualifiers::OCL_Weak) + Diag(DE->getLocation(), diag::error_arc_weak_ivar_access); + } + + return Owned(new (Context) ObjCIvarRefExpr(IV, IV->getType(), + MemberLoc, BaseExpr.take(), + IsArrow)); + } + + // Objective-C property access. + const ObjCObjectPointerType *OPT; + if (!IsArrow && (OPT = BaseType->getAs<ObjCObjectPointerType>())) { + // This actually uses the base as an r-value. + BaseExpr = DefaultLvalueConversion(BaseExpr.take()); + if (BaseExpr.isInvalid()) + return ExprError(); + + assert(Context.hasSameUnqualifiedType(BaseType, BaseExpr.get()->getType())); + + IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); + + const ObjCObjectType *OT = OPT->getObjectType(); + + // id, with and without qualifiers. + if (OT->isObjCId()) { + // Check protocols on qualified interfaces. + Selector Sel = PP.getSelectorTable().getNullarySelector(Member); + if (Decl *PMDecl = FindGetterSetterNameDecl(OPT, Member, Sel, Context)) { + if (ObjCPropertyDecl *PD = dyn_cast<ObjCPropertyDecl>(PMDecl)) { + // Check the use of this declaration + if (DiagnoseUseOfDecl(PD, MemberLoc)) + return ExprError(); + + QualType T = PD->getType(); + if (ObjCMethodDecl *Getter = PD->getGetterMethodDecl()) + T = getMessageSendResultType(BaseType, Getter, false, false); + + return Owned(new (Context) ObjCPropertyRefExpr(PD, T, + VK_LValue, + OK_ObjCProperty, + MemberLoc, + BaseExpr.take())); + } + + if (ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(PMDecl)) { + // Check the use of this method. + if (DiagnoseUseOfDecl(OMD, MemberLoc)) + return ExprError(); + Selector SetterSel = + SelectorTable::constructSetterName(PP.getIdentifierTable(), + PP.getSelectorTable(), Member); + ObjCMethodDecl *SMD = 0; + if (Decl *SDecl = FindGetterSetterNameDecl(OPT, /*Property id*/0, + SetterSel, Context)) + SMD = dyn_cast<ObjCMethodDecl>(SDecl); + QualType PType = getMessageSendResultType(BaseType, OMD, false, + false); + + ExprValueKind VK = VK_LValue; + if (!getLangOptions().CPlusPlus && PType.isCForbiddenLValueType()) + VK = VK_RValue; + ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); + + return Owned(new (Context) ObjCPropertyRefExpr(OMD, SMD, PType, + VK, OK, + MemberLoc, BaseExpr.take())); + } + } + // Use of id.member can only be for a property reference. Do not + // use the 'id' redefinition in this case. + if (IsArrow && ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + + return ExprError(Diag(MemberLoc, diag::err_property_not_found) + << MemberName << BaseType); + } + + // 'Class', unqualified only. + if (OT->isObjCClass()) { + // Only works in a method declaration (??!). + ObjCMethodDecl *MD = getCurMethodDecl(); + if (!MD) { + if (ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + + goto fail; + } + + // Also must look for a getter name which uses property syntax. + Selector Sel = PP.getSelectorTable().getNullarySelector(Member); + ObjCInterfaceDecl *IFace = MD->getClassInterface(); + ObjCMethodDecl *Getter; + if ((Getter = IFace->lookupClassMethod(Sel))) { + // Check the use of this method. + if (DiagnoseUseOfDecl(Getter, MemberLoc)) + return ExprError(); + } else + Getter = IFace->lookupPrivateMethod(Sel, false); + // If we found a getter then this may be a valid dot-reference, we + // will look for the matching setter, in case it is needed. + Selector SetterSel = + SelectorTable::constructSetterName(PP.getIdentifierTable(), + PP.getSelectorTable(), Member); + ObjCMethodDecl *Setter = IFace->lookupClassMethod(SetterSel); + if (!Setter) { + // If this reference is in an @implementation, also check for 'private' + // methods. + Setter = IFace->lookupPrivateMethod(SetterSel, false); + } + // Look through local category implementations associated with the class. + if (!Setter) + Setter = IFace->getCategoryClassMethod(SetterSel); + + if (Setter && DiagnoseUseOfDecl(Setter, MemberLoc)) + return ExprError(); + + if (Getter || Setter) { + QualType PType; + + ExprValueKind VK = VK_LValue; + if (Getter) { + PType = getMessageSendResultType(QualType(OT, 0), Getter, true, + false); + if (!getLangOptions().CPlusPlus && PType.isCForbiddenLValueType()) + VK = VK_RValue; + } else { + // Get the expression type from Setter's incoming parameter. + PType = (*(Setter->param_end() -1))->getType(); + } + ExprObjectKind OK = (VK == VK_RValue ? OK_Ordinary : OK_ObjCProperty); + + // FIXME: we must check that the setter has property type. + return Owned(new (Context) ObjCPropertyRefExpr(Getter, Setter, + PType, VK, OK, + MemberLoc, BaseExpr.take())); + } + + if (ShouldTryAgainWithRedefinitionType(*this, BaseExpr)) + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + + return ExprError(Diag(MemberLoc, diag::err_property_not_found) + << MemberName << BaseType); + } + + // Normal property access. + return HandleExprPropertyRefExpr(OPT, BaseExpr.get(), OpLoc, + MemberName, MemberLoc, + SourceLocation(), QualType(), false); + } + + // Handle 'field access' to vectors, such as 'V.xx'. + if (BaseType->isExtVectorType()) { + // FIXME: this expr should store IsArrow. + IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); + ExprValueKind VK = (IsArrow ? VK_LValue : BaseExpr.get()->getValueKind()); + QualType ret = CheckExtVectorComponent(*this, BaseType, VK, OpLoc, + Member, MemberLoc); + if (ret.isNull()) + return ExprError(); + + return Owned(new (Context) ExtVectorElementExpr(ret, VK, BaseExpr.take(), + *Member, MemberLoc)); + } + + // Adjust builtin-sel to the appropriate redefinition type if that's + // not just a pointer to builtin-sel again. + if (IsArrow && + BaseType->isSpecificBuiltinType(BuiltinType::ObjCSel) && + !Context.ObjCSelRedefinitionType->isObjCSelType()) { + BaseExpr = ImpCastExprToType(BaseExpr.take(), Context.ObjCSelRedefinitionType, + CK_BitCast); + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + } + + // Failure cases. + fail: + + // Recover from dot accesses to pointers, e.g.: + // type *foo; + // foo.bar + // This is actually well-formed in two cases: + // - 'type' is an Objective C type + // - 'bar' is a pseudo-destructor name which happens to refer to + // the appropriate pointer type + if (const PointerType *Ptr = BaseType->getAs<PointerType>()) { + if (!IsArrow && Ptr->getPointeeType()->isRecordType() && + MemberName.getNameKind() != DeclarationName::CXXDestructorName) { + Diag(OpLoc, diag::err_typecheck_member_reference_suggestion) + << BaseType << int(IsArrow) << BaseExpr.get()->getSourceRange() + << FixItHint::CreateReplacement(OpLoc, "->"); + + // Recurse as an -> access. + IsArrow = true; + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + } + } + + // If the user is trying to apply -> or . to a function name, it's probably + // because they forgot parentheses to call that function. + QualType ZeroArgCallTy; + UnresolvedSet<4> Overloads; + if (isExprCallable(*BaseExpr.get(), ZeroArgCallTy, Overloads)) { + if (ZeroArgCallTy.isNull()) { + Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) + << (Overloads.size() > 1) << 0 << BaseExpr.get()->getSourceRange(); + UnresolvedSet<2> PlausibleOverloads; + for (OverloadExpr::decls_iterator It = Overloads.begin(), + DeclsEnd = Overloads.end(); It != DeclsEnd; ++It) { + const FunctionDecl *OverloadDecl = cast<FunctionDecl>(*It); + QualType OverloadResultTy = OverloadDecl->getResultType(); + if ((!IsArrow && OverloadResultTy->isRecordType()) || + (IsArrow && OverloadResultTy->isPointerType() && + OverloadResultTy->getPointeeType()->isRecordType())) + PlausibleOverloads.addDecl(It.getDecl()); + } + NoteOverloads(PlausibleOverloads, BaseExpr.get()->getExprLoc()); + return ExprError(); + } + if ((!IsArrow && ZeroArgCallTy->isRecordType()) || + (IsArrow && ZeroArgCallTy->isPointerType() && + ZeroArgCallTy->getPointeeType()->isRecordType())) { + // At this point, we know BaseExpr looks like it's potentially callable + // with 0 arguments, and that it returns something of a reasonable type, + // so we can emit a fixit and carry on pretending that BaseExpr was + // actually a CallExpr. + SourceLocation ParenInsertionLoc = + PP.getLocForEndOfToken(BaseExpr.get()->getLocEnd()); + Diag(BaseExpr.get()->getExprLoc(), diag::err_member_reference_needs_call) + << (Overloads.size() > 1) << 1 << BaseExpr.get()->getSourceRange() + << FixItHint::CreateInsertion(ParenInsertionLoc, "()"); + // FIXME: Try this before emitting the fixit, and suppress diagnostics + // while doing so. + ExprResult NewBase = + ActOnCallExpr(0, BaseExpr.take(), ParenInsertionLoc, + MultiExprArg(*this, 0, 0), + ParenInsertionLoc.getFileLocWithOffset(1)); + if (NewBase.isInvalid()) + return ExprError(); + BaseExpr = NewBase; + BaseExpr = DefaultFunctionArrayConversion(BaseExpr.take()); + return LookupMemberExpr(R, BaseExpr, IsArrow, OpLoc, SS, + ObjCImpDecl, HasTemplateArgs); + } + } + + Diag(MemberLoc, diag::err_typecheck_member_reference_struct_union) + << BaseType << BaseExpr.get()->getSourceRange(); + + return ExprError(); +} + +/// The main callback when the parser finds something like +/// expression . [nested-name-specifier] identifier +/// expression -> [nested-name-specifier] identifier +/// where 'identifier' encompasses a fairly broad spectrum of +/// possibilities, including destructor and operator references. +/// +/// \param OpKind either tok::arrow or tok::period +/// \param HasTrailingLParen whether the next token is '(', which +/// is used to diagnose mis-uses of special members that can +/// only be called +/// \param ObjCImpDecl the current ObjC @implementation decl; +/// this is an ugly hack around the fact that ObjC @implementations +/// aren't properly put in the context chain +ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, + SourceLocation OpLoc, + tok::TokenKind OpKind, + CXXScopeSpec &SS, + UnqualifiedId &Id, + Decl *ObjCImpDecl, + bool HasTrailingLParen) { + if (SS.isSet() && SS.isInvalid()) + return ExprError(); + + // Warn about the explicit constructor calls Microsoft extension. + if (getLangOptions().Microsoft && + Id.getKind() == UnqualifiedId::IK_ConstructorName) + Diag(Id.getSourceRange().getBegin(), + diag::ext_ms_explicit_constructor_call); + + TemplateArgumentListInfo TemplateArgsBuffer; + + // Decompose the name into its component parts. + DeclarationNameInfo NameInfo; + const TemplateArgumentListInfo *TemplateArgs; + DecomposeUnqualifiedId(Id, TemplateArgsBuffer, + NameInfo, TemplateArgs); + + DeclarationName Name = NameInfo.getName(); + bool IsArrow = (OpKind == tok::arrow); + + NamedDecl *FirstQualifierInScope + = (!SS.isSet() ? 0 : FindFirstQualifierInScope(S, + static_cast<NestedNameSpecifier*>(SS.getScopeRep()))); + + // This is a postfix expression, so get rid of ParenListExprs. + ExprResult Result = MaybeConvertParenListExprToParenExpr(S, Base); + if (Result.isInvalid()) return ExprError(); + Base = Result.take(); + + if (Base->getType()->isDependentType() || Name.isDependentName() || + isDependentScopeSpecifier(SS)) { + Result = ActOnDependentMemberExpr(Base, Base->getType(), + IsArrow, OpLoc, + SS, FirstQualifierInScope, + NameInfo, TemplateArgs); + } else { + LookupResult R(*this, NameInfo, LookupMemberName); + ExprResult BaseResult = Owned(Base); + Result = LookupMemberExpr(R, BaseResult, IsArrow, OpLoc, + SS, ObjCImpDecl, TemplateArgs != 0); + if (BaseResult.isInvalid()) + return ExprError(); + Base = BaseResult.take(); + + if (Result.isInvalid()) { + Owned(Base); + return ExprError(); + } + + if (Result.get()) { + // The only way a reference to a destructor can be used is to + // immediately call it, which falls into this case. If the + // next token is not a '(', produce a diagnostic and build the + // call now. + if (!HasTrailingLParen && + Id.getKind() == UnqualifiedId::IK_DestructorName) + return DiagnoseDtorReference(NameInfo.getLoc(), Result.get()); + + return move(Result); + } + + Result = BuildMemberReferenceExpr(Base, Base->getType(), + OpLoc, IsArrow, SS, FirstQualifierInScope, + R, TemplateArgs); + } + + return move(Result); +} + +static ExprResult +BuildFieldReferenceExpr(Sema &S, Expr *BaseExpr, bool IsArrow, + const CXXScopeSpec &SS, FieldDecl *Field, + DeclAccessPair FoundDecl, + const DeclarationNameInfo &MemberNameInfo) { + // x.a is an l-value if 'a' has a reference type. Otherwise: + // x.a is an l-value/x-value/pr-value if the base is (and note + // that *x is always an l-value), except that if the base isn't + // an ordinary object then we must have an rvalue. + ExprValueKind VK = VK_LValue; + ExprObjectKind OK = OK_Ordinary; + if (!IsArrow) { + if (BaseExpr->getObjectKind() == OK_Ordinary) + VK = BaseExpr->getValueKind(); + else + VK = VK_RValue; + } + if (VK != VK_RValue && Field->isBitField()) + OK = OK_BitField; + + // Figure out the type of the member; see C99 6.5.2.3p3, C++ [expr.ref] + QualType MemberType = Field->getType(); + if (const ReferenceType *Ref = MemberType->getAs<ReferenceType>()) { + MemberType = Ref->getPointeeType(); + VK = VK_LValue; + } else { + QualType BaseType = BaseExpr->getType(); + if (IsArrow) BaseType = BaseType->getAs<PointerType>()->getPointeeType(); + + Qualifiers BaseQuals = BaseType.getQualifiers(); + + // GC attributes are never picked up by members. + BaseQuals.removeObjCGCAttr(); + + // CVR attributes from the base are picked up by members, + // except that 'mutable' members don't pick up 'const'. + if (Field->isMutable()) BaseQuals.removeConst(); + + Qualifiers MemberQuals + = S.Context.getCanonicalType(MemberType).getQualifiers(); + + // TR 18037 does not allow fields to be declared with address spaces. + assert(!MemberQuals.hasAddressSpace()); + + Qualifiers Combined = BaseQuals + MemberQuals; + if (Combined != MemberQuals) + MemberType = S.Context.getQualifiedType(MemberType, Combined); + } + + S.MarkDeclarationReferenced(MemberNameInfo.getLoc(), Field); + ExprResult Base = + S.PerformObjectMemberConversion(BaseExpr, SS.getScopeRep(), + FoundDecl, Field); + if (Base.isInvalid()) + return ExprError(); + return S.Owned(BuildMemberExpr(S.Context, Base.take(), IsArrow, SS, + Field, FoundDecl, MemberNameInfo, + MemberType, VK, OK)); +} + +/// Builds an implicit member access expression. The current context +/// is known to be an instance method, and the given unqualified lookup +/// set is known to contain only instance members, at least one of which +/// is from an appropriate type. +ExprResult +Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS, + LookupResult &R, + const TemplateArgumentListInfo *TemplateArgs, + bool IsKnownInstance) { + assert(!R.empty() && !R.isAmbiguous()); + + SourceLocation loc = R.getNameLoc(); + + // We may have found a field within an anonymous union or struct + // (C++ [class.union]). + // FIXME: template-ids inside anonymous structs? + if (IndirectFieldDecl *FD = R.getAsSingle<IndirectFieldDecl>()) + return BuildAnonymousStructUnionMemberReference(SS, R.getNameLoc(), FD); + + // If this is known to be an instance access, go ahead and build an + // implicit 'this' expression now. + // 'this' expression now. + QualType ThisTy = getAndCaptureCurrentThisType(); + assert(!ThisTy.isNull() && "didn't correctly pre-flight capture of 'this'"); + + Expr *baseExpr = 0; // null signifies implicit access + if (IsKnownInstance) { + SourceLocation Loc = R.getNameLoc(); + if (SS.getRange().isValid()) + Loc = SS.getRange().getBegin(); + baseExpr = new (Context) CXXThisExpr(loc, ThisTy, /*isImplicit=*/true); + } + + return BuildMemberReferenceExpr(baseExpr, ThisTy, + /*OpLoc*/ SourceLocation(), + /*IsArrow*/ true, + SS, + /*FirstQualifierInScope*/ 0, + R, TemplateArgs); +} diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index cb5c1e0d0cbe..02a4682cc840 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -19,6 +19,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtVisitor.h" #include "clang/AST/TypeLoc.h" #include "llvm/ADT/SmallString.h" #include "clang/Lex/Preprocessor.h" @@ -62,7 +63,7 @@ ExprResult Sema::ParseObjCStringLiteral(SourceLocation *AtLocs, // Create the aggregate string with the appropriate content and location // information. - S = StringLiteral::Create(Context, &StrBuf[0], StrBuf.size(), + S = StringLiteral::Create(Context, StrBuf, /*Wide=*/false, /*Pascal=*/false, Context.getPointerType(Context.CharTy), &StrLocs[0], StrLocs.size()); @@ -127,7 +128,8 @@ ExprResult Sema::BuildObjCEncodeExpression(SourceLocation AtLoc, if (EncodedType->isDependentType()) StrTy = Context.DependentTy; else { - if (!EncodedType->getAsArrayTypeUnsafe()) // Incomplete array is handled. + if (!EncodedType->getAsArrayTypeUnsafe() && //// Incomplete array is handled. + !EncodedType->isVoidType()) // void is handled too. if (RequireCompleteType(AtLoc, EncodedType, PDiag(diag::err_incomplete_type_objc_at_encode) << EncodedTypeInfo->getTypeLoc().getSourceRange())) @@ -176,12 +178,39 @@ ExprResult Sema::ParseObjCSelectorExpression(Selector Sel, SourceRange(LParenLoc, RParenLoc)); if (!Method) Diag(SelLoc, diag::warn_undeclared_selector) << Sel; + + if (!Method || + Method->getImplementationControl() != ObjCMethodDecl::Optional) { + llvm::DenseMap<Selector, SourceLocation>::iterator Pos + = ReferencedSelectors.find(Sel); + if (Pos == ReferencedSelectors.end()) + ReferencedSelectors.insert(std::make_pair(Sel, SelLoc)); + } - llvm::DenseMap<Selector, SourceLocation>::iterator Pos - = ReferencedSelectors.find(Sel); - if (Pos == ReferencedSelectors.end()) - ReferencedSelectors.insert(std::make_pair(Sel, SelLoc)); - + // In ARC, forbid the user from using @selector for + // retain/release/autorelease/dealloc/retainCount. + if (getLangOptions().ObjCAutoRefCount) { + switch (Sel.getMethodFamily()) { + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + case OMF_dealloc: + Diag(AtLoc, diag::err_arc_illegal_selector) << + Sel << SourceRange(LParenLoc, RParenLoc); + break; + + case OMF_None: + case OMF_alloc: + case OMF_copy: + case OMF_init: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + case OMF_performSelector: + break; + } + } QualType Ty = Context.getObjCSelType(); return new (Context) ObjCSelectorExpr(Ty, Sel, AtLoc, RParenLoc); } @@ -321,11 +350,22 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, Args[i] = Result.take(); } - unsigned DiagID = isClassMessage ? diag::warn_class_method_not_found : - diag::warn_inst_method_not_found; + unsigned DiagID; + if (getLangOptions().ObjCAutoRefCount) + DiagID = diag::err_arc_method_not_found; + else + DiagID = isClassMessage ? diag::warn_class_method_not_found + : diag::warn_inst_method_not_found; Diag(lbrac, DiagID) << Sel << isClassMessage << SourceRange(lbrac, rbrac); - ReturnType = Context.getObjCIdType(); + + // In debuggers, we want to use __unknown_anytype for these + // results so that clients can cast them. + if (getLangOptions().DebuggerSupport) { + ReturnType = Context.UnknownAnyTy; + } else { + ReturnType = Context.getObjCIdType(); + } VK = VK_RValue; return false; } @@ -404,17 +444,15 @@ bool Sema::CheckMessageArgumentTypes(QualType ReceiverType, return IsError; } -bool Sema::isSelfExpr(Expr *RExpr) { +bool Sema::isSelfExpr(Expr *receiver) { // 'self' is objc 'self' in an objc method only. DeclContext *DC = CurContext; while (isa<BlockDecl>(DC)) DC = DC->getParent(); if (DC && !isa<ObjCMethodDecl>(DC)) return false; - if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(RExpr)) - if (ICE->getCastKind() == CK_LValueToRValue) - RExpr = ICE->getSubExpr(); - if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RExpr)) + receiver = receiver->IgnoreParenLValueCasts(); + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(receiver)) if (DRE->getDecl()->getIdentifier() == &Context.Idents.get("self")) return true; return false; @@ -489,7 +527,8 @@ ObjCMethodDecl *Sema::LookupMethodInQualifiedType(Selector Sel, /// objective C interface. This is a property reference expression. ExprResult Sema:: HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, - Expr *BaseExpr, DeclarationName MemberName, + Expr *BaseExpr, SourceLocation OpLoc, + DeclarationName MemberName, SourceLocation MemberLoc, SourceLocation SuperLoc, QualType SuperType, bool Super) { @@ -635,17 +674,19 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, } // Attempt to correct for typos in property names. - LookupResult Res(*this, MemberName, MemberLoc, LookupOrdinaryName); - if (CorrectTypo(Res, 0, 0, IFace, false, CTC_NoKeywords, OPT) && - Res.getAsSingle<ObjCPropertyDecl>()) { - DeclarationName TypoResult = Res.getLookupName(); + TypoCorrection Corrected = CorrectTypo( + DeclarationNameInfo(MemberName, MemberLoc), LookupOrdinaryName, NULL, + NULL, IFace, false, CTC_NoKeywords, OPT); + if (ObjCPropertyDecl *Property = + Corrected.getCorrectionDeclAs<ObjCPropertyDecl>()) { + DeclarationName TypoResult = Corrected.getCorrection(); Diag(MemberLoc, diag::err_property_not_found_suggest) << MemberName << QualType(OPT, 0) << TypoResult << FixItHint::CreateReplacement(MemberLoc, TypoResult.getAsString()); - ObjCPropertyDecl *Property = Res.getAsSingle<ObjCPropertyDecl>(); Diag(Property->getLocation(), diag::note_previous_decl) << Property->getDeclName(); - return HandleExprPropertyRefExpr(OPT, BaseExpr, TypoResult, MemberLoc, + return HandleExprPropertyRefExpr(OPT, BaseExpr, OpLoc, + TypoResult, MemberLoc, SuperLoc, SuperType, Super); } ObjCInterfaceDecl *ClassDeclared; @@ -663,6 +704,11 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, return ExprError(); } } + Diag(MemberLoc, + diag::err_ivar_access_using_property_syntax_suggest) + << MemberName << QualType(OPT, 0) << Ivar->getDeclName() + << FixItHint::CreateReplacement(OpLoc, "->"); + return ExprError(); } Diag(MemberLoc, diag::err_property_not_found) @@ -699,7 +745,9 @@ ActOnClassPropertyRefExpr(IdentifierInfo &receiverName, T = Context.getObjCObjectPointerType(T); return HandleExprPropertyRefExpr(T->getAsObjCInterfacePointerType(), - /*BaseExpr*/0, &propertyName, + /*BaseExpr*/0, + SourceLocation()/*OpLoc*/, + &propertyName, propertyNameLoc, receiverNameLoc, T, true); } @@ -862,29 +910,30 @@ Sema::ObjCMessageKind Sema::getObjCMessageKind(Scope *S, Method->getClassInterface()->getSuperClass()) CTC = CTC_ObjCMessageReceiver; - if (DeclarationName Corrected = CorrectTypo(Result, S, 0, 0, false, CTC)) { - if (Result.isSingleResult()) { + if (TypoCorrection Corrected = CorrectTypo(Result.getLookupNameInfo(), + Result.getLookupKind(), S, NULL, + NULL, false, CTC)) { + if (NamedDecl *ND = Corrected.getCorrectionDecl()) { // If we found a declaration, correct when it refers to an Objective-C // class. - NamedDecl *ND = Result.getFoundDecl(); if (ObjCInterfaceDecl *Class = dyn_cast<ObjCInterfaceDecl>(ND)) { Diag(NameLoc, diag::err_unknown_receiver_suggest) - << Name << Result.getLookupName() + << Name << Corrected.getCorrection() << FixItHint::CreateReplacement(SourceRange(NameLoc), ND->getNameAsString()); Diag(ND->getLocation(), diag::note_previous_decl) - << Corrected; + << Corrected.getCorrection(); QualType T = Context.getObjCInterfaceType(Class); TypeSourceInfo *TSInfo = Context.getTrivialTypeSourceInfo(T, NameLoc); ReceiverType = CreateParsedType(T, TSInfo); return ObjCClassMessage; } - } else if (Result.empty() && Corrected.getAsIdentifierInfo() && - Corrected.getAsIdentifierInfo()->isStr("super")) { + } else if (Corrected.isKeyword() && + Corrected.getCorrectionAsIdentifierInfo()->isStr("super")) { // If we've found the keyword "super", this is a send to super. Diag(NameLoc, diag::err_unknown_receiver_suggest) - << Name << Corrected + << Name << Corrected.getCorrection() << FixItHint::CreateReplacement(SourceRange(NameLoc), "super"); return ObjCSuperMessage; } @@ -1013,11 +1062,16 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, // Find the method we are messaging. if (!Method) { if (Class->isForwardDecl()) { + if (getLangOptions().ObjCAutoRefCount) { + Diag(Loc, diag::err_arc_receiver_forward_class) << ReceiverType; + } else { + Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName(); + } + // A forward class used in messaging is treated as a 'Class' - Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName(); Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(LBracLoc, RBracLoc)); - if (Method) + if (Method && !getLangOptions().ObjCAutoRefCount) Diag(Method->getLocation(), diag::note_method_sent_forward_class) << Method->getDeclName(); } @@ -1236,6 +1290,14 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, = ReceiverType->getAsObjCInterfacePointerType()) { // We allow sending a message to a pointer to an interface (an object). ClassDecl = OCIType->getInterfaceDecl(); + + if (ClassDecl->isForwardDecl() && getLangOptions().ObjCAutoRefCount) { + Diag(Loc, diag::err_arc_receiver_forward_instance) + << OCIType->getPointeeType() + << (Receiver ? Receiver->getSourceRange() : SourceRange(SuperLoc)); + return ExprError(); + } + // FIXME: consider using LookupInstanceMethodInGlobalPool, since it will be // faster than the following method (which can do *many* linear searches). // The idea is to add class info to MethodPool. @@ -1250,6 +1312,12 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // If we have implementations in scope, check "private" methods. Method = LookupPrivateInstanceMethod(Sel, ClassDecl); + if (!Method && getLangOptions().ObjCAutoRefCount) { + Diag(Loc, diag::err_arc_may_not_respond) + << OCIType->getPointeeType() << Sel; + return ExprError(); + } + if (!Method && (!Receiver || !isSelfExpr(Receiver))) { // If we still haven't found a method, look in the global pool. This // behavior isn't very desirable, however we need it for GCC @@ -1267,10 +1335,12 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, } if (Method && DiagnoseUseOfDecl(Method, Loc, forwardClass)) return ExprError(); - } else if (!Context.getObjCIdType().isNull() && + } else if (!getLangOptions().ObjCAutoRefCount && + !Context.getObjCIdType().isNull() && (ReceiverType->isPointerType() || ReceiverType->isIntegerType())) { // Implicitly convert integers and pointers to 'id' but emit a warning. + // But not in ARC. Diag(Loc, diag::warn_bad_receiver_type) << ReceiverType << Receiver->getSourceRange(); @@ -1332,8 +1402,84 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, diag::err_illegal_message_expr_incomplete_type)) return ExprError(); + // In ARC, forbid the user from sending messages to + // retain/release/autorelease/dealloc/retainCount explicitly. + if (getLangOptions().ObjCAutoRefCount) { + ObjCMethodFamily family = + (Method ? Method->getMethodFamily() : Sel.getMethodFamily()); + switch (family) { + case OMF_init: + if (Method) + checkInitMethod(Method, ReceiverType); + + case OMF_None: + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + break; + + case OMF_dealloc: + case OMF_retain: + case OMF_release: + case OMF_autorelease: + case OMF_retainCount: + Diag(Loc, diag::err_arc_illegal_explicit_message) + << Sel << SelectorLoc; + break; + + case OMF_performSelector: + if (Method && NumArgs >= 1) { + if (ObjCSelectorExpr *SelExp = dyn_cast<ObjCSelectorExpr>(Args[0])) { + Selector ArgSel = SelExp->getSelector(); + ObjCMethodDecl *SelMethod = + LookupInstanceMethodInGlobalPool(ArgSel, + SelExp->getSourceRange()); + if (!SelMethod) + SelMethod = + LookupFactoryMethodInGlobalPool(ArgSel, + SelExp->getSourceRange()); + if (SelMethod) { + ObjCMethodFamily SelFamily = SelMethod->getMethodFamily(); + switch (SelFamily) { + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + case OMF_self: + case OMF_init: + // Issue error, unless ns_returns_not_retained. + if (!SelMethod->hasAttr<NSReturnsNotRetainedAttr>()) { + // selector names a +1 method + Diag(SelectorLoc, + diag::err_arc_perform_selector_retains); + Diag(SelMethod->getLocation(), diag::note_method_declared_at); + } + break; + default: + // +0 call. OK. unless ns_returns_retained. + if (SelMethod->hasAttr<NSReturnsRetainedAttr>()) { + // selector names a +1 method + Diag(SelectorLoc, + diag::err_arc_perform_selector_retains); + Diag(SelMethod->getLocation(), diag::note_method_declared_at); + } + break; + } + } + } else { + // error (may leak). + Diag(SelectorLoc, diag::warn_arc_perform_selector_leaks); + Diag(Args[0]->getExprLoc(), diag::note_used_here); + } + } + break; + } + } + // Construct the appropriate ObjCMessageExpr instance. - Expr *Result; + ObjCMessageExpr *Result; if (SuperLoc.isValid()) Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, SuperLoc, /*IsInstanceSuper=*/true, @@ -1343,6 +1489,27 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, Result = ObjCMessageExpr::Create(Context, ReturnType, VK, LBracLoc, Receiver, Sel, SelectorLoc, Method, Args, NumArgs, RBracLoc); + + if (getLangOptions().ObjCAutoRefCount) { + // In ARC, annotate delegate init calls. + if (Result->getMethodFamily() == OMF_init && + (SuperLoc.isValid() || isSelfExpr(Receiver))) { + // Only consider init calls *directly* in init implementations, + // not within blocks. + ObjCMethodDecl *method = dyn_cast<ObjCMethodDecl>(CurContext); + if (method && method->getMethodFamily() == OMF_init) { + // The implicit assignment to self means we also don't want to + // consume the result. + Result->setDelegateInitCall(true); + return Owned(Result); + } + } + + // In ARC, check for message sends which are likely to introduce + // retain cycles. + checkRetainCycles(Result); + } + return MaybeBindToTemporary(Result); } @@ -1364,3 +1531,393 @@ ExprResult Sema::ActOnInstanceMessage(Scope *S, LBracLoc, SelectorLoc, RBracLoc, move(Args)); } +enum ARCConversionTypeClass { + ACTC_none, + ACTC_retainable, + ACTC_indirectRetainable +}; +static ARCConversionTypeClass classifyTypeForARCConversion(QualType type) { + ARCConversionTypeClass ACTC = ACTC_retainable; + + // Ignore an outermost reference type. + if (const ReferenceType *ref = type->getAs<ReferenceType>()) + type = ref->getPointeeType(); + + // Drill through pointers and arrays recursively. + while (true) { + if (const PointerType *ptr = type->getAs<PointerType>()) { + type = ptr->getPointeeType(); + } else if (const ArrayType *array = type->getAsArrayTypeUnsafe()) { + type = QualType(array->getElementType()->getBaseElementTypeUnsafe(), 0); + } else { + break; + } + ACTC = ACTC_indirectRetainable; + } + + if (!type->isObjCRetainableType()) return ACTC_none; + return ACTC; +} + +namespace { + /// Return true if the given expression can be reasonably converted + /// between a retainable pointer type and a C pointer type. + struct ARCCastChecker : StmtVisitor<ARCCastChecker, bool> { + ASTContext &Context; + ARCCastChecker(ASTContext &Context) : Context(Context) {} + bool VisitStmt(Stmt *s) { + return false; + } + bool VisitExpr(Expr *e) { + return e->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); + } + + bool VisitParenExpr(ParenExpr *e) { + return Visit(e->getSubExpr()); + } + bool VisitCastExpr(CastExpr *e) { + switch (e->getCastKind()) { + case CK_NullToPointer: + return true; + case CK_NoOp: + case CK_LValueToRValue: + case CK_BitCast: + case CK_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + return Visit(e->getSubExpr()); + default: + return false; + } + } + bool VisitUnaryExtension(UnaryOperator *e) { + return Visit(e->getSubExpr()); + } + bool VisitBinComma(BinaryOperator *e) { + return Visit(e->getRHS()); + } + bool VisitConditionalOperator(ConditionalOperator *e) { + // Conditional operators are okay if both sides are okay. + return Visit(e->getTrueExpr()) && Visit(e->getFalseExpr()); + } + bool VisitObjCStringLiteral(ObjCStringLiteral *e) { + // Always white-list Objective-C string literals. + return true; + } + bool VisitStmtExpr(StmtExpr *e) { + return Visit(e->getSubStmt()->body_back()); + } + bool VisitDeclRefExpr(DeclRefExpr *e) { + // White-list references to global extern strings from system + // headers. + if (VarDecl *var = dyn_cast<VarDecl>(e->getDecl())) + if (var->getStorageClass() == SC_Extern && + var->getType().isConstQualified() && + Context.getSourceManager().isInSystemHeader(var->getLocation())) + return true; + return false; + } + }; +} + +bool +Sema::ValidObjCARCNoBridgeCastExpr(Expr *&Exp, QualType castType) { + Expr *NewExp = Exp->IgnoreParenCasts(); + + if (!isa<ObjCMessageExpr>(NewExp) && !isa<ObjCPropertyRefExpr>(NewExp) + && !isa<CallExpr>(NewExp)) + return false; + ObjCMethodDecl *method = 0; + bool MethodReturnsPlusOne = false; + + if (ObjCPropertyRefExpr *PRE = dyn_cast<ObjCPropertyRefExpr>(NewExp)) { + method = PRE->getExplicitProperty()->getGetterMethodDecl(); + } + else if (ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(NewExp)) + method = ME->getMethodDecl(); + else { + CallExpr *CE = cast<CallExpr>(NewExp); + Decl *CallDecl = CE->getCalleeDecl(); + if (!CallDecl) + return false; + if (CallDecl->hasAttr<CFReturnsNotRetainedAttr>()) + return true; + MethodReturnsPlusOne = CallDecl->hasAttr<CFReturnsRetainedAttr>(); + if (!MethodReturnsPlusOne) { + if (NamedDecl *ND = dyn_cast<NamedDecl>(CallDecl)) + if (const IdentifierInfo *Id = ND->getIdentifier()) + if (Id->isStr("__builtin___CFStringMakeConstantString")) + return true; + } + } + + if (!MethodReturnsPlusOne) { + if (!method) + return false; + if (method->hasAttr<CFReturnsNotRetainedAttr>()) + return true; + MethodReturnsPlusOne = method->hasAttr<CFReturnsRetainedAttr>(); + if (!MethodReturnsPlusOne) { + ObjCMethodFamily family = method->getSelector().getMethodFamily(); + switch (family) { + case OMF_alloc: + case OMF_copy: + case OMF_mutableCopy: + case OMF_new: + MethodReturnsPlusOne = true; + break; + default: + break; + } + } + } + + if (MethodReturnsPlusOne) { + TypeSourceInfo *TSInfo = + Context.getTrivialTypeSourceInfo(castType, SourceLocation()); + ExprResult ExpRes = BuildObjCBridgedCast(SourceLocation(), OBC_BridgeTransfer, + SourceLocation(), TSInfo, Exp); + Exp = ExpRes.take(); + } + return true; +} + +void +Sema::CheckObjCARCConversion(SourceRange castRange, QualType castType, + Expr *&castExpr, CheckedConversionKind CCK) { + QualType castExprType = castExpr->getType(); + + ARCConversionTypeClass exprACTC = classifyTypeForARCConversion(castExprType); + ARCConversionTypeClass castACTC = classifyTypeForARCConversion(castType); + if (exprACTC == castACTC) return; + if (exprACTC && castType->isIntegralType(Context)) return; + + // Allow casts between pointers to lifetime types (e.g., __strong id*) + // and pointers to void (e.g., cv void *). Casting from void* to lifetime* + // must be explicit. + if (const PointerType *CastPtr = castType->getAs<PointerType>()) { + if (const PointerType *CastExprPtr = castExprType->getAs<PointerType>()) { + QualType CastPointee = CastPtr->getPointeeType(); + QualType CastExprPointee = CastExprPtr->getPointeeType(); + if ((CCK != CCK_ImplicitConversion && + CastPointee->isObjCIndirectLifetimeType() && + CastExprPointee->isVoidType()) || + (CastPointee->isVoidType() && + CastExprPointee->isObjCIndirectLifetimeType())) + return; + } + } + + if (ARCCastChecker(Context).Visit(castExpr)) + return; + + SourceLocation loc = + (castRange.isValid() ? castRange.getBegin() : castExpr->getExprLoc()); + + if (makeUnavailableInSystemHeader(loc, + "converts between Objective-C and C pointers in -fobjc-arc")) + return; + + unsigned srcKind = 0; + switch (exprACTC) { + case ACTC_none: + srcKind = (castExprType->isPointerType() ? 1 : 0); + break; + case ACTC_retainable: + srcKind = (castExprType->isBlockPointerType() ? 2 : 3); + break; + case ACTC_indirectRetainable: + srcKind = 4; + break; + } + + if (CCK == CCK_CStyleCast) { + // Check whether this could be fixed with a bridge cast. + SourceLocation AfterLParen = PP.getLocForEndOfToken(castRange.getBegin()); + SourceLocation NoteLoc = AfterLParen.isValid()? AfterLParen : loc; + + if (castType->isObjCARCBridgableType() && + castExprType->isCARCBridgableType()) { + // explicit unbridged casts are allowed if the source of the cast is a + // message sent to an objc method (or property access) + if (ValidObjCARCNoBridgeCastExpr(castExpr, castType)) + return; + Diag(loc, diag::err_arc_cast_requires_bridge) + << 2 + << castExprType + << (castType->isBlockPointerType()? 1 : 0) + << castType + << castRange + << castExpr->getSourceRange(); + Diag(NoteLoc, diag::note_arc_bridge) + << FixItHint::CreateInsertion(AfterLParen, "__bridge "); + Diag(NoteLoc, diag::note_arc_bridge_transfer) + << castExprType + << FixItHint::CreateInsertion(AfterLParen, "__bridge_transfer "); + + return; + } + + if (castType->isCARCBridgableType() && + castExprType->isObjCARCBridgableType()){ + Diag(loc, diag::err_arc_cast_requires_bridge) + << (castExprType->isBlockPointerType()? 1 : 0) + << castExprType + << 2 + << castType + << castRange + << castExpr->getSourceRange(); + + Diag(NoteLoc, diag::note_arc_bridge) + << FixItHint::CreateInsertion(AfterLParen, "__bridge "); + Diag(NoteLoc, diag::note_arc_bridge_retained) + << castType + << FixItHint::CreateInsertion(AfterLParen, "__bridge_retained "); + return; + } + } + + Diag(loc, diag::err_arc_mismatched_cast) + << (CCK != CCK_ImplicitConversion) << srcKind << castExprType << castType + << castRange << castExpr->getSourceRange(); +} + +bool Sema::CheckObjCARCUnavailableWeakConversion(QualType castType, + QualType exprType) { + QualType canCastType = + Context.getCanonicalType(castType).getUnqualifiedType(); + QualType canExprType = + Context.getCanonicalType(exprType).getUnqualifiedType(); + if (isa<ObjCObjectPointerType>(canCastType) && + castType.getObjCLifetime() == Qualifiers::OCL_Weak && + canExprType->isObjCObjectPointerType()) { + if (const ObjCObjectPointerType *ObjT = + canExprType->getAs<ObjCObjectPointerType>()) + if (ObjT->getInterfaceDecl()->isArcWeakrefUnavailable()) + return false; + } + return true; +} + +/// Look for an ObjCReclaimReturnedObject cast and destroy it. +static Expr *maybeUndoReclaimObject(Expr *e) { + // For now, we just undo operands that are *immediately* reclaim + // expressions, which prevents the vast majority of potential + // problems here. To catch them all, we'd need to rebuild arbitrary + // value-propagating subexpressions --- we can't reliably rebuild + // in-place because of expression sharing. + if (ImplicitCastExpr *ice = dyn_cast<ImplicitCastExpr>(e)) + if (ice->getCastKind() == CK_ObjCReclaimReturnedObject) + return ice->getSubExpr(); + + return e; +} + +ExprResult Sema::BuildObjCBridgedCast(SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + TypeSourceInfo *TSInfo, + Expr *SubExpr) { + QualType T = TSInfo->getType(); + QualType FromType = SubExpr->getType(); + + bool MustConsume = false; + if (T->isDependentType() || SubExpr->isTypeDependent()) { + // Okay: we'll build a dependent expression type. + } else if (T->isObjCARCBridgableType() && FromType->isCARCBridgableType()) { + // Casting CF -> id + switch (Kind) { + case OBC_Bridge: + break; + + case OBC_BridgeRetained: + Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) + << 2 + << FromType + << (T->isBlockPointerType()? 1 : 0) + << T + << SubExpr->getSourceRange() + << Kind; + Diag(BridgeKeywordLoc, diag::note_arc_bridge) + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge"); + Diag(BridgeKeywordLoc, diag::note_arc_bridge_transfer) + << FromType + << FixItHint::CreateReplacement(BridgeKeywordLoc, + "__bridge_transfer "); + + Kind = OBC_Bridge; + break; + + case OBC_BridgeTransfer: + // We must consume the Objective-C object produced by the cast. + MustConsume = true; + break; + } + } else if (T->isCARCBridgableType() && FromType->isObjCARCBridgableType()) { + // Okay: id -> CF + switch (Kind) { + case OBC_Bridge: + // Reclaiming a value that's going to be __bridge-casted to CF + // is very dangerous, so we don't do it. + SubExpr = maybeUndoReclaimObject(SubExpr); + break; + + case OBC_BridgeRetained: + // Produce the object before casting it. + SubExpr = ImplicitCastExpr::Create(Context, FromType, + CK_ObjCProduceObject, + SubExpr, 0, VK_RValue); + break; + + case OBC_BridgeTransfer: + Diag(BridgeKeywordLoc, diag::err_arc_bridge_cast_wrong_kind) + << (FromType->isBlockPointerType()? 1 : 0) + << FromType + << 2 + << T + << SubExpr->getSourceRange() + << Kind; + + Diag(BridgeKeywordLoc, diag::note_arc_bridge) + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge "); + Diag(BridgeKeywordLoc, diag::note_arc_bridge_retained) + << T + << FixItHint::CreateReplacement(BridgeKeywordLoc, "__bridge_retained "); + + Kind = OBC_Bridge; + break; + } + } else { + Diag(LParenLoc, diag::err_arc_bridge_cast_incompatible) + << FromType << T << Kind + << SubExpr->getSourceRange() + << TSInfo->getTypeLoc().getSourceRange(); + return ExprError(); + } + + Expr *Result = new (Context) ObjCBridgedCastExpr(LParenLoc, Kind, + BridgeKeywordLoc, + TSInfo, SubExpr); + + if (MustConsume) { + ExprNeedsCleanups = true; + Result = ImplicitCastExpr::Create(Context, T, CK_ObjCConsumeObject, Result, + 0, VK_RValue); + } + + return Result; +} + +ExprResult Sema::ActOnObjCBridgedCast(Scope *S, + SourceLocation LParenLoc, + ObjCBridgeCastKind Kind, + SourceLocation BridgeKeywordLoc, + ParsedType Type, + SourceLocation RParenLoc, + Expr *SubExpr) { + TypeSourceInfo *TSInfo = 0; + QualType T = GetTypeFromParser(Type, &TSInfo); + if (!TSInfo) + TSInfo = Context.getTrivialTypeSourceInfo(T, LParenLoc); + return BuildObjCBridgedCast(LParenLoc, Kind, BridgeKeywordLoc, TSInfo, + SubExpr); +} diff --git a/lib/Sema/SemaInit.cpp b/lib/Sema/SemaInit.cpp index a33f5d0b2f3e..9fbcbab0b7d0 100644 --- a/lib/Sema/SemaInit.cpp +++ b/lib/Sema/SemaInit.cpp @@ -769,7 +769,8 @@ void InitListChecker::CheckSubElementType(const InitializedEntity &Entity, // subaggregate, brace elision is assumed and the initializer is // considered for the initialization of the first member of // the subaggregate. - if (ElemType->isAggregateType() || ElemType->isVectorType()) { + if (!SemaRef.getLangOptions().OpenCL && + (ElemType->isAggregateType() || ElemType->isVectorType())) { CheckImplicitInitList(Entity, IList, ElemType, Index, StructuredList, StructuredIndex); ++StructuredIndex; @@ -1184,6 +1185,15 @@ void InitListChecker::CheckStructUnionTypes(const InitializedEntity &Entity, continue; } + // Make sure we can use this declaration. + if (SemaRef.DiagnoseUseOfDecl(*Field, + IList->getInit(Index)->getLocStart())) { + ++Index; + ++Field; + hadError = true; + continue; + } + InitializedEntity MemberEntity = InitializedEntity::InitializeMember(*Field, &Entity); CheckSubElementType(MemberEntity, IList, Field->getType(), Index, @@ -1443,19 +1453,23 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, // was a typo for another field name. LookupResult R(SemaRef, FieldName, D->getFieldLoc(), Sema::LookupMemberName); - if (SemaRef.CorrectTypo(R, /*Scope=*/0, /*SS=*/0, RT->getDecl(), false, - Sema::CTC_NoKeywords) && - (ReplacementField = R.getAsSingle<FieldDecl>()) && + TypoCorrection Corrected = SemaRef.CorrectTypo( + DeclarationNameInfo(FieldName, D->getFieldLoc()), + Sema::LookupMemberName, /*Scope=*/NULL, /*SS=*/NULL, + RT->getDecl(), false, Sema::CTC_NoKeywords); + if ((ReplacementField = Corrected.getCorrectionDeclAs<FieldDecl>()) && ReplacementField->getDeclContext()->getRedeclContext() ->Equals(RT->getDecl())) { + std::string CorrectedStr( + Corrected.getAsString(SemaRef.getLangOptions())); + std::string CorrectedQuotedStr( + Corrected.getQuoted(SemaRef.getLangOptions())); SemaRef.Diag(D->getFieldLoc(), diag::err_field_designator_unknown_suggest) - << FieldName << CurrentObjectType << R.getLookupName() - << FixItHint::CreateReplacement(D->getFieldLoc(), - R.getLookupName().getAsString()); + << FieldName << CurrentObjectType << CorrectedQuotedStr + << FixItHint::CreateReplacement(D->getFieldLoc(), CorrectedStr); SemaRef.Diag(ReplacementField->getLocation(), - diag::note_previous_decl) - << ReplacementField->getDeclName(); + diag::note_previous_decl) << CorrectedQuotedStr; } else { SemaRef.Diag(D->getFieldLoc(), diag::err_field_designator_unknown) << FieldName << CurrentObjectType; @@ -1499,6 +1513,12 @@ InitListChecker::CheckDesignatedInitializer(const InitializedEntity &Entity, StructuredList->setInitializedFieldInUnion(*Field); } + // Make sure we can use this declaration. + if (SemaRef.DiagnoseUseOfDecl(*Field, D->getFieldLoc())) { + ++Index; + return true; + } + // Update the designator with the field declaration. D->setField(*Field); @@ -2029,10 +2049,10 @@ InitializedEntity InitializedEntity::InitializeBase(ASTContext &Context, DeclarationName InitializedEntity::getName() const { switch (getKind()) { - case EK_Parameter: - if (!VariableOrMember) - return DeclarationName(); - // Fall through + case EK_Parameter: { + ParmVarDecl *D = reinterpret_cast<ParmVarDecl*>(Parameter & ~0x1); + return (D ? D->getDeclName() : DeclarationName()); + } case EK_Variable: case EK_Member: @@ -2057,10 +2077,12 @@ DeclarationName InitializedEntity::getName() const { DeclaratorDecl *InitializedEntity::getDecl() const { switch (getKind()) { case EK_Variable: - case EK_Parameter: case EK_Member: return VariableOrMember; + case EK_Parameter: + return reinterpret_cast<ParmVarDecl*>(Parameter & ~0x1); + case EK_Result: case EK_Exception: case EK_New: @@ -2123,6 +2145,9 @@ void InitializationSequence::Step::Destroy() { case SK_StringInit: case SK_ObjCObjectConversion: case SK_ArrayInit: + case SK_PassByIndirectCopyRestore: + case SK_PassByIndirectRestore: + case SK_ProduceObjCObject: break; case SK_ConversionSequence: @@ -2131,7 +2156,7 @@ void InitializationSequence::Step::Destroy() { } bool InitializationSequence::isDirectReferenceBinding() const { - return getKind() == ReferenceBinding && Steps.back().Kind == SK_BindReference; + return !Steps.empty() && Steps.back().Kind == SK_BindReference; } bool InitializationSequence::isAmbiguous() const { @@ -2306,6 +2331,22 @@ void InitializationSequence::AddArrayInitStep(QualType T) { Steps.push_back(S); } +void InitializationSequence::AddPassByIndirectCopyRestoreStep(QualType type, + bool shouldCopy) { + Step s; + s.Kind = (shouldCopy ? SK_PassByIndirectCopyRestore + : SK_PassByIndirectRestore); + s.Type = type; + Steps.push_back(s); +} + +void InitializationSequence::AddProduceObjCObjectStep(QualType T) { + Step S; + S.Kind = SK_ProduceObjCObject; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::SetOverloadFailure(FailureKind Failure, OverloadingResult Result) { setSequenceKind(FailedSequence); @@ -2317,6 +2358,33 @@ void InitializationSequence::SetOverloadFailure(FailureKind Failure, // Attempt initialization //===----------------------------------------------------------------------===// +static void MaybeProduceObjCObject(Sema &S, + InitializationSequence &Sequence, + const InitializedEntity &Entity) { + if (!S.getLangOptions().ObjCAutoRefCount) return; + + /// When initializing a parameter, produce the value if it's marked + /// __attribute__((ns_consumed)). + if (Entity.getKind() == InitializedEntity::EK_Parameter) { + if (!Entity.isParameterConsumed()) + return; + + assert(Entity.getType()->isObjCRetainableType() && + "consuming an object of unretainable type?"); + Sequence.AddProduceObjCObjectStep(Entity.getType()); + + /// When initializing a return value, if the return type is a + /// retainable type, then returns need to immediately retain the + /// object. If an autorelease is required, it will be done at the + /// last instant. + } else if (Entity.getKind() == InitializedEntity::EK_Result) { + if (!Entity.getType()->isObjCRetainableType()) + return; + + Sequence.AddProduceObjCObjectStep(Entity.getType()); + } +} + /// \brief Attempt list initialization (C++0x [dcl.init.list]) static void TryListInitialization(Sema &S, const InitializedEntity &Entity, @@ -2380,13 +2448,16 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S, bool DerivedToBase; bool ObjCConversion; + bool ObjCLifetimeConversion; assert(!S.CompareReferenceRelationship(Initializer->getLocStart(), T1, T2, DerivedToBase, - ObjCConversion) && + ObjCConversion, + ObjCLifetimeConversion) && "Must have incompatible references when binding via conversion"); (void)DerivedToBase; (void)ObjCConversion; - + (void)ObjCLifetimeConversion; + // Build the candidate set directly in the initialization sequence // structure, so that it will persist if we fail. OverloadCandidateSet &CandidateSet = Sequence.getFailedCandidateSet(); @@ -2513,10 +2584,12 @@ static OverloadingResult TryRefInitWithConversionFunction(Sema &S, bool NewDerivedToBase = false; bool NewObjCConversion = false; + bool NewObjCLifetimeConversion = false; Sema::ReferenceCompareResult NewRefRelationship = S.CompareReferenceRelationship(DeclLoc, T1, T2.getNonLValueExprType(S.Context), - NewDerivedToBase, NewObjCConversion); + NewDerivedToBase, NewObjCConversion, + NewObjCLifetimeConversion); if (NewRefRelationship == Sema::Ref_Incompatible) { // If the type we've converted to is not reference-related to the // type we're looking for, then there is another conversion step @@ -2550,8 +2623,6 @@ static void TryReferenceInitialization(Sema &S, const InitializationKind &Kind, Expr *Initializer, InitializationSequence &Sequence) { - Sequence.setSequenceKind(InitializationSequence::ReferenceBinding); - QualType DestType = Entity.getType(); QualType cv1T1 = DestType->getAs<ReferenceType>()->getPointeeType(); Qualifiers T1Quals; @@ -2584,10 +2655,11 @@ static void TryReferenceInitialization(Sema &S, bool isRValueRef = !isLValueRef; bool DerivedToBase = false; bool ObjCConversion = false; + bool ObjCLifetimeConversion = false; Expr::Classification InitCategory = Initializer->Classify(S.Context); Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship(DeclLoc, cv1T1, cv2T2, DerivedToBase, - ObjCConversion); + ObjCConversion, ObjCLifetimeConversion); // C++0x [dcl.init.ref]p5: // A reference to type "cv1 T1" is initialized by an expression of type @@ -2746,11 +2818,15 @@ static void TryReferenceInitialization(Sema &S, InitializedEntity TempEntity = InitializedEntity::InitializeTemporary(cv1T1); - if (S.TryImplicitConversion(Sequence, TempEntity, Initializer, + ImplicitConversionSequence ICS + = S.TryImplicitConversion(Initializer, TempEntity.getType(), /*SuppressUserConversions*/ false, AllowExplicit, /*FIXME:InOverloadResolution=*/false, - /*CStyle=*/Kind.isCStyleOrFunctionalCast())) { + /*CStyle=*/Kind.isCStyleOrFunctionalCast(), + /*AllowObjCWritebackConversion=*/false); + + if (ICS.isBad()) { // FIXME: Use the conversion function set stored in ICS to turn // this into an overloading ambiguity diagnostic. However, we need // to keep that set as an OverloadCandidateSet rather than as some @@ -2764,6 +2840,8 @@ static void TryReferenceInitialization(Sema &S, else Sequence.SetFailed(InitializationSequence::FK_ReferenceInitFailed); return; + } else { + Sequence.AddConversionSequenceStep(ICS, TempEntity.getType()); } // [...] If T1 is reference-related to T2, cv1 must be the @@ -2953,10 +3031,8 @@ static void TryDefaultInitialization(Sema &S, // C++ [dcl.init]p6: // To default-initialize an object of type T means: // - if T is an array type, each element is default-initialized; - QualType DestType = Entity.getType(); - while (const ArrayType *Array = S.Context.getAsArrayType(DestType)) - DestType = Array->getElementType(); - + QualType DestType = S.Context.getBaseElementType(Entity.getType()); + // - if T is a (possibly cv-qualified) class type (Clause 9), the default // constructor for T is called (and the initialization is ill-formed if // T has no accessible default constructor); @@ -2970,8 +3046,16 @@ static void TryDefaultInitialization(Sema &S, // If a program calls for the default initialization of an object of // a const-qualified type T, T shall be a class type with a user-provided // default constructor. - if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus) + if (DestType.isConstQualified() && S.getLangOptions().CPlusPlus) { Sequence.SetFailed(InitializationSequence::FK_DefaultInitOfConst); + return; + } + + // If the destination type has a lifetime property, zero-initialize it. + if (DestType.getQualifiers().hasObjCLifetime()) { + Sequence.AddZeroInitializationStep(Entity.getType()); + return; + } } /// \brief Attempt a user-defined conversion between two types (C++ [dcl.init]), @@ -3125,6 +3209,86 @@ static void TryUserDefinedConversion(Sema &S, } } +/// The non-zero enum values here are indexes into diagnostic alternatives. +enum InvalidICRKind { IIK_okay, IIK_nonlocal, IIK_nonscalar }; + +/// Determines whether this expression is an acceptable ICR source. +static InvalidICRKind isInvalidICRSource(ASTContext &C, Expr *e, + bool isAddressOf) { + // Skip parens. + e = e->IgnoreParens(); + + // Skip address-of nodes. + if (UnaryOperator *op = dyn_cast<UnaryOperator>(e)) { + if (op->getOpcode() == UO_AddrOf) + return isInvalidICRSource(C, op->getSubExpr(), /*addressof*/ true); + + // Skip certain casts. + } else if (CastExpr *ce = dyn_cast<CastExpr>(e)) { + switch (ce->getCastKind()) { + case CK_Dependent: + case CK_BitCast: + case CK_LValueBitCast: + case CK_NoOp: + return isInvalidICRSource(C, ce->getSubExpr(), isAddressOf); + + case CK_ArrayToPointerDecay: + return IIK_nonscalar; + + case CK_NullToPointer: + return IIK_okay; + + default: + break; + } + + // If we have a declaration reference, it had better be a local variable. + } else if (isa<DeclRefExpr>(e) || isa<BlockDeclRefExpr>(e)) { + if (!isAddressOf) return IIK_nonlocal; + + VarDecl *var; + if (isa<DeclRefExpr>(e)) { + var = dyn_cast<VarDecl>(cast<DeclRefExpr>(e)->getDecl()); + if (!var) return IIK_nonlocal; + } else { + var = cast<BlockDeclRefExpr>(e)->getDecl(); + } + + return (var->hasLocalStorage() ? IIK_okay : IIK_nonlocal); + + // If we have a conditional operator, check both sides. + } else if (ConditionalOperator *cond = dyn_cast<ConditionalOperator>(e)) { + if (InvalidICRKind iik = isInvalidICRSource(C, cond->getLHS(), isAddressOf)) + return iik; + + return isInvalidICRSource(C, cond->getRHS(), isAddressOf); + + // These are never scalar. + } else if (isa<ArraySubscriptExpr>(e)) { + return IIK_nonscalar; + + // Otherwise, it needs to be a null pointer constant. + } else { + return (e->isNullPointerConstant(C, Expr::NPC_ValueDependentIsNull) + ? IIK_okay : IIK_nonlocal); + } + + return IIK_nonlocal; +} + +/// Check whether the given expression is a valid operand for an +/// indirect copy/restore. +static void checkIndirectCopyRestoreSource(Sema &S, Expr *src) { + assert(src->isRValue()); + + InvalidICRKind iik = isInvalidICRSource(S.Context, src, false); + if (iik == IIK_okay) return; + + S.Diag(src->getExprLoc(), diag::err_arc_nonlocal_writeback) + << ((unsigned) iik - 1) // shift index into diagnostic explanations + << src->getSourceRange(); +} + /// \brief Determine whether we have compatible array types for the /// purposes of GNU by-copy array initialization. static bool hasCompatibleArrayTypes(ASTContext &Context, @@ -3144,6 +3308,53 @@ static bool hasCompatibleArrayTypes(ASTContext &Context, return Source->isConstantArrayType() && Dest->isIncompleteArrayType(); } +static bool tryObjCWritebackConversion(Sema &S, + InitializationSequence &Sequence, + const InitializedEntity &Entity, + Expr *Initializer) { + bool ArrayDecay = false; + QualType ArgType = Initializer->getType(); + QualType ArgPointee; + if (const ArrayType *ArgArrayType = S.Context.getAsArrayType(ArgType)) { + ArrayDecay = true; + ArgPointee = ArgArrayType->getElementType(); + ArgType = S.Context.getPointerType(ArgPointee); + } + + // Handle write-back conversion. + QualType ConvertedArgType; + if (!S.isObjCWritebackConversion(ArgType, Entity.getType(), + ConvertedArgType)) + return false; + + // We should copy unless we're passing to an argument explicitly + // marked 'out'. + bool ShouldCopy = true; + if (ParmVarDecl *param = cast_or_null<ParmVarDecl>(Entity.getDecl())) + ShouldCopy = (param->getObjCDeclQualifier() != ParmVarDecl::OBJC_TQ_Out); + + // Do we need an lvalue conversion? + if (ArrayDecay || Initializer->isGLValue()) { + ImplicitConversionSequence ICS; + ICS.setStandard(); + ICS.Standard.setAsIdentityConversion(); + + QualType ResultType; + if (ArrayDecay) { + ICS.Standard.First = ICK_Array_To_Pointer; + ResultType = S.Context.getPointerType(ArgPointee); + } else { + ICS.Standard.First = ICK_Lvalue_To_Rvalue; + ResultType = Initializer->getType().getNonLValueExprType(S.Context); + } + + Sequence.AddConversionSequenceStep(ICS, ResultType); + } + + Sequence.AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy); + return true; +} + InitializationSequence::InitializationSequence(Sema &S, const InitializedEntity &Entity, const InitializationKind &Kind, @@ -3255,12 +3466,28 @@ InitializationSequence::InitializationSequence(Sema &S, return; } - // Handle initialization in C + // Determine whether we should consider writeback conversions for + // Objective-C ARC. + bool allowObjCWritebackConversion = S.getLangOptions().ObjCAutoRefCount && + Entity.getKind() == InitializedEntity::EK_Parameter; + + // We're at the end of the line for C: it's either a write-back conversion + // or it's a C assignment. There's no need to check anything else. if (!S.getLangOptions().CPlusPlus) { + // If allowed, check whether this is an Objective-C writeback conversion. + if (allowObjCWritebackConversion && + tryObjCWritebackConversion(S, *this, Entity, Initializer)) { + return; + } + + // Handle initialization in C AddCAssignmentStep(DestType); + MaybeProduceObjCObject(S, *this, Entity); return; } + assert(S.getLangOptions().CPlusPlus); + // - If the destination type is a (possibly cv-qualified) class type: if (DestType->isRecordType()) { // - If the initialization is direct-initialization, or if it is @@ -3294,6 +3521,7 @@ InitializationSequence::InitializationSequence(Sema &S, // type, conversion functions are considered. if (!SourceType.isNull() && SourceType->isRecordType()) { TryUserDefinedConversion(S, Entity, Kind, Initializer, *this); + MaybeProduceObjCObject(S, *this, Entity); return; } @@ -3302,12 +3530,38 @@ InitializationSequence::InitializationSequence(Sema &S, // conversions (Clause 4) will be used, if necessary, to convert the // initializer expression to the cv-unqualified version of the // destination type; no user-defined conversions are considered. - if (S.TryImplicitConversion(*this, Entity, Initializer, - /*SuppressUserConversions*/ true, + + ImplicitConversionSequence ICS + = S.TryImplicitConversion(Initializer, Entity.getType(), + /*SuppressUserConversions*/true, /*AllowExplicitConversions*/ false, /*InOverloadResolution*/ false, - /*CStyle=*/Kind.isCStyleOrFunctionalCast())) - { + /*CStyle=*/Kind.isCStyleOrFunctionalCast(), + allowObjCWritebackConversion); + + if (ICS.isStandard() && + ICS.Standard.Second == ICK_Writeback_Conversion) { + // Objective-C ARC writeback conversion. + + // We should copy unless we're passing to an argument explicitly + // marked 'out'. + bool ShouldCopy = true; + if (ParmVarDecl *Param = cast_or_null<ParmVarDecl>(Entity.getDecl())) + ShouldCopy = (Param->getObjCDeclQualifier() != ParmVarDecl::OBJC_TQ_Out); + + // If there was an lvalue adjustment, add it as a separate conversion. + if (ICS.Standard.First == ICK_Array_To_Pointer || + ICS.Standard.First == ICK_Lvalue_To_Rvalue) { + ImplicitConversionSequence LvalueICS; + LvalueICS.setStandard(); + LvalueICS.Standard.setAsIdentityConversion(); + LvalueICS.Standard.setAllToTypes(ICS.Standard.getToType(0)); + LvalueICS.Standard.First = ICS.Standard.First; + AddConversionSequenceStep(LvalueICS, ICS.Standard.getToType(0)); + } + + AddPassByIndirectCopyRestoreStep(Entity.getType(), ShouldCopy); + } else if (ICS.isBad()) { DeclAccessPair dap; if (Initializer->getType() == Context.OverloadTy && !S.ResolveAddressOfOverloadedFunction(Initializer @@ -3315,6 +3569,10 @@ InitializationSequence::InitializationSequence(Sema &S, SetFailed(InitializationSequence::FK_AddressOfOverloadFailed); else SetFailed(InitializationSequence::FK_ConversionFailed); + } else { + AddConversionSequenceStep(ICS, Entity.getType()); + + MaybeProduceObjCObject(S, *this, Entity); } } @@ -3560,7 +3818,7 @@ static ExprResult CopyObject(Sema &S, << (int)Entity.getKind() << CurInitExpr->getType() << CurInitExpr->getSourceRange(); S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << Best->Function->isDeleted(); + << 1 << Best->Function->isDeleted(); return ExprError(); } @@ -3633,6 +3891,11 @@ void InitializationSequence::PrintInitLocationNote(Sema &S, } } +static bool isReferenceBinding(const InitializationSequence::Step &s) { + return s.Kind == InitializationSequence::SK_BindReference || + s.Kind == InitializationSequence::SK_BindReferenceToTemporary; +} + ExprResult InitializationSequence::Perform(Sema &S, const InitializedEntity &Entity, @@ -3684,19 +3947,9 @@ InitializationSequence::Perform(Sema &S, } } - - if (Kind.getKind() == InitializationKind::IK_Copy || Kind.isExplicitCast()) - return ExprResult(Args.release()[0]); - - if (Args.size() == 0) - return S.Owned((Expr *)0); - - unsigned NumArgs = Args.size(); - return S.Owned(new (S.Context) ParenListExpr(S.Context, - SourceLocation(), - (Expr **)Args.release(), - NumArgs, - SourceLocation())); + assert(Kind.getKind() == InitializationKind::IK_Copy || + Kind.isExplicitCast()); + return ExprResult(Args.release()[0]); } // No steps means no initialization. @@ -3733,7 +3986,10 @@ InitializationSequence::Perform(Sema &S, case SK_CAssignment: case SK_StringInit: case SK_ObjCObjectConversion: - case SK_ArrayInit: { + case SK_ArrayInit: + case SK_PassByIndirectCopyRestore: + case SK_PassByIndirectRestore: + case SK_ProduceObjCObject: { assert(Args.size() == 1); CurInit = Args.get()[0]; if (!CurInit.get()) return ExprError(); @@ -3841,12 +4097,22 @@ InitializationSequence::Perform(Sema &S, break; case SK_BindReferenceToTemporary: - // Reference binding does not have any corresponding ASTs. - // Check exception specifications if (S.CheckExceptionSpecCompatibility(CurInit.get(), DestType)) return ExprError(); + // Materialize the temporary into memory. + CurInit = new (S.Context) MaterializeTemporaryExpr( + Entity.getType().getNonReferenceType(), + CurInit.get(), + Entity.getType()->isLValueReferenceType()); + + // If we're binding to an Objective-C object that has lifetime, we + // need cleanups. + if (S.getLangOptions().ObjCAutoRefCount && + CurInit.get()->getType()->isObjCLifetimeType()) + S.ExprNeedsCleanups = true; + break; case SK_ExtraneousCopyToTemporary: @@ -3925,8 +4191,7 @@ InitializationSequence::Perform(Sema &S, CreatedObject = Conversion->getResultType()->isRecordType(); } - bool RequiresCopy = !IsCopy && - getKind() != InitializationSequence::ReferenceBinding; + bool RequiresCopy = !IsCopy && !isReferenceBinding(Steps.back()); if (RequiresCopy || shouldBindAsTemporary(Entity)) CurInit = S.MaybeBindToTemporary(CurInit.takeAs<Expr>()); else if (CreatedObject && shouldDestroyTemporary(Entity)) { @@ -3969,10 +4234,14 @@ InitializationSequence::Perform(Sema &S, } case SK_ConversionSequence: { + Sema::CheckedConversionKind CCK + = Kind.isCStyleCast()? Sema::CCK_CStyleCast + : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast + : Kind.isExplicitCast()? Sema::CCK_OtherCast + : Sema::CCK_ImplicitConversion; ExprResult CurInitExprRes = S.PerformImplicitConversion(CurInit.get(), Step->Type, *Step->ICS, - getAssignmentAction(Entity), - Kind.isCStyleOrFunctionalCast()); + getAssignmentAction(Entity), CCK); if (CurInitExprRes.isInvalid()) return ExprError(); CurInit = move(CurInitExprRes); @@ -4182,7 +4451,20 @@ InitializationSequence::Perform(Sema &S, } } } + break; + case SK_PassByIndirectCopyRestore: + case SK_PassByIndirectRestore: + checkIndirectCopyRestoreSource(S, CurInit.get()); + CurInit = S.Owned(new (S.Context) + ObjCIndirectCopyRestoreExpr(CurInit.take(), Step->Type, + Step->Kind == SK_PassByIndirectCopyRestore)); + break; + + case SK_ProduceObjCObject: + CurInit = S.Owned(ImplicitCastExpr::Create(S.Context, Step->Type, + CK_ObjCProduceObject, + CurInit.take(), 0, VK_RValue)); break; } } @@ -4278,7 +4560,7 @@ bool InitializationSequence::Diagnose(Sema &S, true); if (Ovl == OR_Deleted) { S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << Best->Function->isDeleted(); + << 1 << Best->Function->isDeleted(); } else { llvm_unreachable("Inconsistent overload resolution?"); } @@ -4443,7 +4725,7 @@ bool InitializationSequence::Diagnose(Sema &S, = FailedCandidateSet.BestViableFunction(S, Kind.getLocation(), Best); if (Ovl == OR_Deleted) { S.Diag(Best->Function->getLocation(), diag::note_unavailable_here) - << Best->Function->isDeleted(); + << 1 << Best->Function->isDeleted(); } else { llvm_unreachable("Inconsistent overload resolution?"); } @@ -4587,10 +4869,6 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const { case NormalSequence: OS << "Normal sequence: "; break; - - case ReferenceBinding: - OS << "Reference binding: "; - break; } for (step_iterator S = step_begin(), SEnd = step_end(); S != SEnd; ++S) { @@ -4674,6 +4952,18 @@ void InitializationSequence::dump(llvm::raw_ostream &OS) const { case SK_ArrayInit: OS << "array initialization"; break; + + case SK_PassByIndirectCopyRestore: + OS << "pass by indirect copy and restore"; + break; + + case SK_PassByIndirectRestore: + OS << "pass by indirect restore"; + break; + + case SK_ProduceObjCObject: + OS << "Objective-C object retension"; + break; } } } diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 92ade1efcf01..0e448e31207d 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -20,6 +20,7 @@ #include "clang/Sema/ScopeInfo.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/Sema/ExternalSemaSource.h" +#include "clang/Sema/TypoCorrection.h" #include "clang/AST/ASTContext.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/Decl.h" @@ -42,6 +43,7 @@ #include <iterator> #include <utility> #include <algorithm> +#include <map> using namespace clang; using namespace sema; @@ -207,6 +209,7 @@ static inline unsigned getIDNS(Sema::LookupNameKind NameKind, bool Redeclaration) { unsigned IDNS = 0; switch (NameKind) { + case Sema::LookupObjCImplicitSelfParam: case Sema::LookupOrdinaryName: case Sema::LookupRedeclarationWithLinkage: IDNS = Decl::IDNS_Ordinary; @@ -1095,7 +1098,10 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool AllowBuiltinCreation) { if (LeftStartingScope && !((*I)->hasLinkage())) continue; } - + else if (NameKind == LookupObjCImplicitSelfParam && + !isa<ImplicitParamDecl>(*I)) + continue; + R.addDecl(*I); if ((*I)->getAttr<OverloadableAttr>()) { @@ -1379,6 +1385,7 @@ bool Sema::LookupQualifiedName(LookupResult &R, DeclContext *LookupCtx, // Look for this member in our base classes CXXRecordDecl::BaseMatchesCallback *BaseCallback = 0; switch (R.getLookupKind()) { + case LookupObjCImplicitSelfParam: case LookupOrdinaryName: case LookupMemberName: case LookupRedeclarationWithLinkage: @@ -2137,15 +2144,15 @@ void Sema::LookupOverloadedOperatorName(OverloadedOperatorKind Op, Scope *S, } } -Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, +Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *RD, CXXSpecialMember SM, bool ConstArg, bool VolatileArg, bool RValueThis, bool ConstThis, bool VolatileThis) { - D = D->getDefinition(); - assert((D && !D->isBeingDefined()) && + RD = RD->getDefinition(); + assert((RD && !RD->isBeingDefined()) && "doing special member lookup into record that isn't fully complete"); if (RValueThis || ConstThis || VolatileThis) assert((SM == CXXCopyAssignment || SM == CXXMoveAssignment) && @@ -2155,7 +2162,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, "parameter-less special members can't have qualified arguments"); llvm::FoldingSetNodeID ID; - ID.AddPointer(D); + ID.AddPointer(RD); ID.AddInteger(SM); ID.AddInteger(ConstArg); ID.AddInteger(VolatileArg); @@ -2176,9 +2183,9 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, SpecialMemberCache.InsertNode(Result, InsertPoint); if (SM == CXXDestructor) { - if (!D->hasDeclaredDestructor()) - DeclareImplicitDestructor(D); - CXXDestructorDecl *DD = D->getDestructor(); + if (!RD->hasDeclaredDestructor()) + DeclareImplicitDestructor(RD); + CXXDestructorDecl *DD = RD->getDestructor(); assert(DD && "record without a destructor"); Result->setMethod(DD); Result->setSuccess(DD->isDeleted()); @@ -2188,7 +2195,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, // Prepare for overload resolution. Here we construct a synthetic argument // if necessary and make sure that implicit functions are declared. - CanQualType CanTy = Context.getCanonicalType(Context.getTagDeclType(D)); + CanQualType CanTy = Context.getCanonicalType(Context.getTagDeclType(RD)); DeclarationName Name; Expr *Arg = 0; unsigned NumArgs; @@ -2196,18 +2203,18 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, if (SM == CXXDefaultConstructor) { Name = Context.DeclarationNames.getCXXConstructorName(CanTy); NumArgs = 0; - if (D->needsImplicitDefaultConstructor()) - DeclareImplicitDefaultConstructor(D); + if (RD->needsImplicitDefaultConstructor()) + DeclareImplicitDefaultConstructor(RD); } else { if (SM == CXXCopyConstructor || SM == CXXMoveConstructor) { Name = Context.DeclarationNames.getCXXConstructorName(CanTy); - if (!D->hasDeclaredCopyConstructor()) - DeclareImplicitCopyConstructor(D); + if (!RD->hasDeclaredCopyConstructor()) + DeclareImplicitCopyConstructor(RD); // TODO: Move constructors } else { Name = Context.DeclarationNames.getCXXOperatorName(OO_Equal); - if (!D->hasDeclaredCopyAssignment()) - DeclareImplicitCopyAssignment(D); + if (!RD->hasDeclaredCopyAssignment()) + DeclareImplicitCopyAssignment(RD); // TODO: Move assignment } @@ -2225,7 +2232,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, // there is no semantic difference for class types in this restricted // case. ExprValueKind VK; - if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + if (SM == CXXCopyConstructor || SM == CXXCopyAssignment) VK = VK_LValue; else VK = VK_RValue; @@ -2240,7 +2247,7 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, ThisTy.addConst(); if (VolatileThis) ThisTy.addVolatile(); - Expr::Classification ObjectClassification = + Expr::Classification Classification = (new (Context) OpaqueValueExpr(SourceLocation(), ThisTy, RValueThis ? VK_RValue : VK_LValue))-> Classify(Context); @@ -2252,16 +2259,33 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, DeclContext::lookup_iterator I, E; Result->setConstParamMatch(false); - llvm::tie(I, E) = D->lookup(Name); + llvm::tie(I, E) = RD->lookup(Name); assert((I != E) && "lookup for a constructor or assignment operator was empty"); for ( ; I != E; ++I) { - if ((*I)->isInvalidDecl()) + Decl *Cand = *I; + + if (Cand->isInvalidDecl()) continue; - if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(*I)) { - AddOverloadCandidate(M, DeclAccessPair::make(M, AS_public), &Arg, NumArgs, - OCS, true); + if (UsingShadowDecl *U = dyn_cast<UsingShadowDecl>(Cand)) { + // FIXME: [namespace.udecl]p15 says that we should only consider a + // using declaration here if it does not match a declaration in the + // derived class. We do not implement this correctly in other cases + // either. + Cand = U->getTargetDecl(); + + if (Cand->isInvalidDecl()) + continue; + } + + if (CXXMethodDecl *M = dyn_cast<CXXMethodDecl>(Cand)) { + if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + AddMethodCandidate(M, DeclAccessPair::make(M, AS_public), RD, ThisTy, + Classification, &Arg, NumArgs, OCS, true); + else + AddOverloadCandidate(M, DeclAccessPair::make(M, AS_public), &Arg, + NumArgs, OCS, true); // Here we're looking for a const parameter to speed up creation of // implicit copy methods. @@ -2269,13 +2293,21 @@ Sema::SpecialMemberOverloadResult *Sema::LookupSpecialMember(CXXRecordDecl *D, (SM == CXXCopyConstructor && cast<CXXConstructorDecl>(M)->isCopyConstructor())) { QualType ArgType = M->getType()->getAs<FunctionProtoType>()->getArgType(0); - if (ArgType->getPointeeType().isConstQualified()) + if (!ArgType->isReferenceType() || + ArgType->getPointeeType().isConstQualified()) Result->setConstParamMatch(true); } + } else if (FunctionTemplateDecl *Tmpl = + dyn_cast<FunctionTemplateDecl>(Cand)) { + if (SM == CXXCopyAssignment || SM == CXXMoveAssignment) + AddMethodTemplateCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), + RD, 0, ThisTy, Classification, &Arg, NumArgs, + OCS, true); + else + AddTemplateOverloadCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), + 0, &Arg, NumArgs, OCS, true); } else { - FunctionTemplateDecl *Tmpl = cast<FunctionTemplateDecl>(*I); - AddTemplateOverloadCandidate(Tmpl, DeclAccessPair::make(Tmpl, AS_public), - 0, &Arg, NumArgs, OCS, true); + assert(isa<UsingDecl>(Cand) && "illegal Kind of operator = Decl"); } } @@ -2310,10 +2342,10 @@ CXXConstructorDecl *Sema::LookupDefaultConstructor(CXXRecordDecl *Class) { return cast_or_null<CXXConstructorDecl>(Result->getMethod()); } -/// \brief Look up the copy constructor for the given class. -CXXConstructorDecl *Sema::LookupCopyConstructor(CXXRecordDecl *Class, - unsigned Quals, - bool *ConstParamMatch) { +/// \brief Look up the copying constructor for the given class. +CXXConstructorDecl *Sema::LookupCopyingConstructor(CXXRecordDecl *Class, + unsigned Quals, + bool *ConstParamMatch) { assert(!(Quals & ~(Qualifiers::Const | Qualifiers::Volatile)) && "non-const, non-volatile qualifiers for copy ctor arg"); SpecialMemberOverloadResult *Result = @@ -2341,6 +2373,27 @@ DeclContext::lookup_result Sema::LookupConstructors(CXXRecordDecl *Class) { return Class->lookup(Name); } +/// \brief Look up the copying assignment operator for the given class. +CXXMethodDecl *Sema::LookupCopyingAssignment(CXXRecordDecl *Class, + unsigned Quals, bool RValueThis, + unsigned ThisQuals, + bool *ConstParamMatch) { + assert(!(Quals & ~(Qualifiers::Const | Qualifiers::Volatile)) && + "non-const, non-volatile qualifiers for copy assignment arg"); + assert(!(ThisQuals & ~(Qualifiers::Const | Qualifiers::Volatile)) && + "non-const, non-volatile qualifiers for copy assignment this"); + SpecialMemberOverloadResult *Result = + LookupSpecialMember(Class, CXXCopyAssignment, Quals & Qualifiers::Const, + Quals & Qualifiers::Volatile, RValueThis, + ThisQuals & Qualifiers::Const, + ThisQuals & Qualifiers::Volatile); + + if (ConstParamMatch) + *ConstParamMatch = Result->hasConstParamMatch(); + + return Result->getMethod(); +} + /// \brief Look for the destructor of the given class. /// /// During semantic analysis, this routine should be used in lieu of @@ -2995,6 +3048,12 @@ LabelDecl *Sema::LookupOrCreateLabel(IdentifierInfo *II, SourceLocation Loc, //===----------------------------------------------------------------------===// namespace { + +typedef llvm::StringMap<TypoCorrection, llvm::BumpPtrAllocator> TypoResultsMap; +typedef std::map<unsigned, TypoResultsMap *> TypoEditDistanceMap; + +static const unsigned MaxTypoDistanceResultSets = 5; + class TypoCorrectionConsumer : public VisibleDeclConsumer { /// \brief The name written that is a typo in the source. llvm::StringRef Typo; @@ -3002,33 +3061,55 @@ class TypoCorrectionConsumer : public VisibleDeclConsumer { /// \brief The results found that have the smallest edit distance /// found (so far) with the typo name. /// - /// The boolean value indicates whether there is a keyword with this name. - llvm::StringMap<bool, llvm::BumpPtrAllocator> BestResults; + /// The pointer value being set to the current DeclContext indicates + /// whether there is a keyword with this name. + TypoEditDistanceMap BestResults; + + /// \brief The worst of the best N edit distances found so far. + unsigned MaxEditDistance; - /// \brief The best edit distance found so far. - unsigned BestEditDistance; + Sema &SemaRef; public: - explicit TypoCorrectionConsumer(IdentifierInfo *Typo) + explicit TypoCorrectionConsumer(Sema &SemaRef, IdentifierInfo *Typo) : Typo(Typo->getName()), - BestEditDistance((std::numeric_limits<unsigned>::max)()) { } + MaxEditDistance((std::numeric_limits<unsigned>::max)()), + SemaRef(SemaRef) { } + ~TypoCorrectionConsumer() { + for (TypoEditDistanceMap::iterator I = BestResults.begin(), + IEnd = BestResults.end(); + I != IEnd; + ++I) + delete I->second; + } + virtual void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, bool InBaseClass); void FoundName(llvm::StringRef Name); - void addKeywordResult(ASTContext &Context, llvm::StringRef Keyword); - - typedef llvm::StringMap<bool, llvm::BumpPtrAllocator>::iterator iterator; - iterator begin() { return BestResults.begin(); } - iterator end() { return BestResults.end(); } - void erase(iterator I) { BestResults.erase(I); } + void addKeywordResult(llvm::StringRef Keyword); + void addName(llvm::StringRef Name, NamedDecl *ND, unsigned Distance, + NestedNameSpecifier *NNS=NULL); + void addCorrection(TypoCorrection Correction); + + typedef TypoResultsMap::iterator result_iterator; + typedef TypoEditDistanceMap::iterator distance_iterator; + distance_iterator begin() { return BestResults.begin(); } + distance_iterator end() { return BestResults.end(); } + void erase(distance_iterator I) { BestResults.erase(I); } unsigned size() const { return BestResults.size(); } bool empty() const { return BestResults.empty(); } - bool &operator[](llvm::StringRef Name) { - return BestResults[Name]; + TypoCorrection &operator[](llvm::StringRef Name) { + return (*BestResults.begin()->second)[Name]; + } + + unsigned getMaxEditDistance() const { + return MaxEditDistance; } - unsigned getBestEditDistance() const { return BestEditDistance; } + unsigned getBestEditDistance() { + return (BestResults.empty()) ? MaxEditDistance : BestResults.begin()->first; + } }; } @@ -3053,55 +3134,181 @@ void TypoCorrectionConsumer::FoundName(llvm::StringRef Name) { // Use a simple length-based heuristic to determine the minimum possible // edit distance. If the minimum isn't good enough, bail out early. unsigned MinED = abs((int)Name.size() - (int)Typo.size()); - if (MinED > BestEditDistance || (MinED && Typo.size() / MinED < 3)) + if (MinED > MaxEditDistance || (MinED && Typo.size() / MinED < 3)) return; // Compute an upper bound on the allowable edit distance, so that the // edit-distance algorithm can short-circuit. unsigned UpperBound = - std::min(unsigned((Typo.size() + 2) / 3), BestEditDistance); + std::min(unsigned((Typo.size() + 2) / 3), MaxEditDistance); // Compute the edit distance between the typo and the name of this // entity. If this edit distance is not worse than the best edit // distance we've seen so far, add it to the list of results. unsigned ED = Typo.edit_distance(Name, true, UpperBound); - if (ED == 0) - return; - if (ED < BestEditDistance) { - // This result is better than any we've seen before; clear out - // the previous results. - BestResults.clear(); - BestEditDistance = ED; - } else if (ED > BestEditDistance) { + if (ED > MaxEditDistance) { // This result is worse than the best results we've seen so far; // ignore it. return; } - // Add this name to the list of results. By not assigning a value, we - // keep the current value if we've seen this name before (either as a - // keyword or as a declaration), or get the default value (not a keyword) - // if we haven't seen it before. - (void)BestResults[Name]; + addName(Name, NULL, ED); } -void TypoCorrectionConsumer::addKeywordResult(ASTContext &Context, - llvm::StringRef Keyword) { +void TypoCorrectionConsumer::addKeywordResult(llvm::StringRef Keyword) { // Compute the edit distance between the typo and this keyword. // If this edit distance is not worse than the best edit // distance we've seen so far, add it to the list of results. unsigned ED = Typo.edit_distance(Keyword); - if (ED < BestEditDistance) { - BestResults.clear(); - BestEditDistance = ED; - } else if (ED > BestEditDistance) { + if (ED > MaxEditDistance) { // This result is worse than the best results we've seen so far; // ignore it. return; } - BestResults[Keyword] = true; + addName(Keyword, TypoCorrection::KeywordDecl(), ED); +} + +void TypoCorrectionConsumer::addName(llvm::StringRef Name, + NamedDecl *ND, + unsigned Distance, + NestedNameSpecifier *NNS) { + addCorrection(TypoCorrection(&SemaRef.Context.Idents.get(Name), + ND, NNS, Distance)); +} + +void TypoCorrectionConsumer::addCorrection(TypoCorrection Correction) { + llvm::StringRef Name = Correction.getCorrectionAsIdentifierInfo()->getName(); + TypoResultsMap *& Map = BestResults[Correction.getEditDistance()]; + if (!Map) + Map = new TypoResultsMap; + + TypoCorrection &CurrentCorrection = (*Map)[Name]; + if (!CurrentCorrection || + // FIXME: The following should be rolled up into an operator< on + // TypoCorrection with a more principled definition. + CurrentCorrection.isKeyword() < Correction.isKeyword() || + Correction.getAsString(SemaRef.getLangOptions()) < + CurrentCorrection.getAsString(SemaRef.getLangOptions())) + CurrentCorrection = Correction; + + while (BestResults.size() > MaxTypoDistanceResultSets) { + TypoEditDistanceMap::iterator Last = BestResults.end(); + --Last; + delete Last->second; + BestResults.erase(Last); + } +} + +namespace { + +class SpecifierInfo { + public: + DeclContext* DeclCtx; + NestedNameSpecifier* NameSpecifier; + unsigned EditDistance; + + SpecifierInfo(DeclContext *Ctx, NestedNameSpecifier *NNS, unsigned ED) + : DeclCtx(Ctx), NameSpecifier(NNS), EditDistance(ED) {} +}; + +typedef llvm::SmallVector<DeclContext*, 4> DeclContextList; +typedef llvm::SmallVector<SpecifierInfo, 16> SpecifierInfoList; + +class NamespaceSpecifierSet { + ASTContext &Context; + DeclContextList CurContextChain; + bool isSorted; + + SpecifierInfoList Specifiers; + llvm::SmallSetVector<unsigned, 4> Distances; + llvm::DenseMap<unsigned, SpecifierInfoList> DistanceMap; + + /// \brief Helper for building the list of DeclContexts between the current + /// context and the top of the translation unit + static DeclContextList BuildContextChain(DeclContext *Start); + + void SortNamespaces(); + + public: + explicit NamespaceSpecifierSet(ASTContext &Context, DeclContext *CurContext) + : Context(Context), CurContextChain(BuildContextChain(CurContext)), + isSorted(true) {} + + /// \brief Add the namespace to the set, computing the corresponding + /// NestedNameSpecifier and its distance in the process. + void AddNamespace(NamespaceDecl *ND); + + typedef SpecifierInfoList::iterator iterator; + iterator begin() { + if (!isSorted) SortNamespaces(); + return Specifiers.begin(); + } + iterator end() { return Specifiers.end(); } +}; + +} + +DeclContextList NamespaceSpecifierSet::BuildContextChain(DeclContext *Start) { + assert(Start && "Bulding a context chain from a null context"); + DeclContextList Chain; + for (DeclContext *DC = Start->getPrimaryContext(); DC != NULL; + DC = DC->getLookupParent()) { + NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(DC); + if (!DC->isInlineNamespace() && !DC->isTransparentContext() && + !(ND && ND->isAnonymousNamespace())) + Chain.push_back(DC->getPrimaryContext()); + } + return Chain; +} + +void NamespaceSpecifierSet::SortNamespaces() { + llvm::SmallVector<unsigned, 4> sortedDistances; + sortedDistances.append(Distances.begin(), Distances.end()); + + if (sortedDistances.size() > 1) + std::sort(sortedDistances.begin(), sortedDistances.end()); + + Specifiers.clear(); + for (llvm::SmallVector<unsigned, 4>::iterator DI = sortedDistances.begin(), + DIEnd = sortedDistances.end(); + DI != DIEnd; ++DI) { + SpecifierInfoList &SpecList = DistanceMap[*DI]; + Specifiers.append(SpecList.begin(), SpecList.end()); + } + + isSorted = true; +} + +void NamespaceSpecifierSet::AddNamespace(NamespaceDecl *ND) { + DeclContext *Ctx = cast<DeclContext>(ND); + NestedNameSpecifier *NNS = NULL; + unsigned NumSpecifiers = 0; + DeclContextList NamespaceDeclChain(BuildContextChain(Ctx)); + + // Eliminate common elements from the two DeclContext chains + for (DeclContextList::reverse_iterator C = CurContextChain.rbegin(), + CEnd = CurContextChain.rend(); + C != CEnd && !NamespaceDeclChain.empty() && + NamespaceDeclChain.back() == *C; ++C) { + NamespaceDeclChain.pop_back(); + } + + // Build the NestedNameSpecifier from what is left of the NamespaceDeclChain + for (DeclContextList::reverse_iterator C = NamespaceDeclChain.rbegin(), + CEnd = NamespaceDeclChain.rend(); + C != CEnd; ++C) { + NamespaceDecl *ND = dyn_cast_or_null<NamespaceDecl>(*C); + if (ND) { + NNS = NestedNameSpecifier::Create(Context, NNS, ND); + ++NumSpecifiers; + } + } + + isSorted = false; + Distances.insert(NumSpecifiers); + DistanceMap[NumSpecifiers].push_back(SpecifierInfo(Ctx, NNS, NumSpecifiers)); } /// \brief Perform name lookup for a possible result for typo correction. @@ -3155,14 +3362,193 @@ static void LookupPotentialTypoResult(Sema &SemaRef, } } +/// \brief Add keywords to the consumer as possible typo corrections. +static void AddKeywordsToConsumer(Sema &SemaRef, + TypoCorrectionConsumer &Consumer, + Scope *S, Sema::CorrectTypoContext CTC) { + // Add context-dependent keywords. + bool WantTypeSpecifiers = false; + bool WantExpressionKeywords = false; + bool WantCXXNamedCasts = false; + bool WantRemainingKeywords = false; + switch (CTC) { + case Sema::CTC_Unknown: + WantTypeSpecifiers = true; + WantExpressionKeywords = true; + WantCXXNamedCasts = true; + WantRemainingKeywords = true; + + if (ObjCMethodDecl *Method = SemaRef.getCurMethodDecl()) + if (Method->getClassInterface() && + Method->getClassInterface()->getSuperClass()) + Consumer.addKeywordResult("super"); + + break; + + case Sema::CTC_NoKeywords: + break; + + case Sema::CTC_Type: + WantTypeSpecifiers = true; + break; + + case Sema::CTC_ObjCMessageReceiver: + Consumer.addKeywordResult("super"); + // Fall through to handle message receivers like expressions. + + case Sema::CTC_Expression: + if (SemaRef.getLangOptions().CPlusPlus) + WantTypeSpecifiers = true; + WantExpressionKeywords = true; + // Fall through to get C++ named casts. + + case Sema::CTC_CXXCasts: + WantCXXNamedCasts = true; + break; + + case Sema::CTC_ObjCPropertyLookup: + // FIXME: Add "isa"? + break; + + case Sema::CTC_MemberLookup: + if (SemaRef.getLangOptions().CPlusPlus) + Consumer.addKeywordResult("template"); + break; + + case Sema::CTC_ObjCIvarLookup: + break; + } + + if (WantTypeSpecifiers) { + // Add type-specifier keywords to the set of results. + const char *CTypeSpecs[] = { + "char", "const", "double", "enum", "float", "int", "long", "short", + "signed", "struct", "union", "unsigned", "void", "volatile", + "_Complex", "_Imaginary", + // storage-specifiers as well + "extern", "inline", "static", "typedef" + }; + + const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]); + for (unsigned I = 0; I != NumCTypeSpecs; ++I) + Consumer.addKeywordResult(CTypeSpecs[I]); + + if (SemaRef.getLangOptions().C99) + Consumer.addKeywordResult("restrict"); + if (SemaRef.getLangOptions().Bool || SemaRef.getLangOptions().CPlusPlus) + Consumer.addKeywordResult("bool"); + else if (SemaRef.getLangOptions().C99) + Consumer.addKeywordResult("_Bool"); + + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("class"); + Consumer.addKeywordResult("typename"); + Consumer.addKeywordResult("wchar_t"); + + if (SemaRef.getLangOptions().CPlusPlus0x) { + Consumer.addKeywordResult("char16_t"); + Consumer.addKeywordResult("char32_t"); + Consumer.addKeywordResult("constexpr"); + Consumer.addKeywordResult("decltype"); + Consumer.addKeywordResult("thread_local"); + } + } + + if (SemaRef.getLangOptions().GNUMode) + Consumer.addKeywordResult("typeof"); + } + + if (WantCXXNamedCasts && SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("const_cast"); + Consumer.addKeywordResult("dynamic_cast"); + Consumer.addKeywordResult("reinterpret_cast"); + Consumer.addKeywordResult("static_cast"); + } + + if (WantExpressionKeywords) { + Consumer.addKeywordResult("sizeof"); + if (SemaRef.getLangOptions().Bool || SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("false"); + Consumer.addKeywordResult("true"); + } + + if (SemaRef.getLangOptions().CPlusPlus) { + const char *CXXExprs[] = { + "delete", "new", "operator", "throw", "typeid" + }; + const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]); + for (unsigned I = 0; I != NumCXXExprs; ++I) + Consumer.addKeywordResult(CXXExprs[I]); + + if (isa<CXXMethodDecl>(SemaRef.CurContext) && + cast<CXXMethodDecl>(SemaRef.CurContext)->isInstance()) + Consumer.addKeywordResult("this"); + + if (SemaRef.getLangOptions().CPlusPlus0x) { + Consumer.addKeywordResult("alignof"); + Consumer.addKeywordResult("nullptr"); + } + } + } + + if (WantRemainingKeywords) { + if (SemaRef.getCurFunctionOrMethodDecl() || SemaRef.getCurBlock()) { + // Statements. + const char *CStmts[] = { + "do", "else", "for", "goto", "if", "return", "switch", "while" }; + const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]); + for (unsigned I = 0; I != NumCStmts; ++I) + Consumer.addKeywordResult(CStmts[I]); + + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("catch"); + Consumer.addKeywordResult("try"); + } + + if (S && S->getBreakParent()) + Consumer.addKeywordResult("break"); + + if (S && S->getContinueParent()) + Consumer.addKeywordResult("continue"); + + if (!SemaRef.getCurFunction()->SwitchStack.empty()) { + Consumer.addKeywordResult("case"); + Consumer.addKeywordResult("default"); + } + } else { + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("namespace"); + Consumer.addKeywordResult("template"); + } + + if (S && S->isClassScope()) { + Consumer.addKeywordResult("explicit"); + Consumer.addKeywordResult("friend"); + Consumer.addKeywordResult("mutable"); + Consumer.addKeywordResult("private"); + Consumer.addKeywordResult("protected"); + Consumer.addKeywordResult("public"); + Consumer.addKeywordResult("virtual"); + } + } + + if (SemaRef.getLangOptions().CPlusPlus) { + Consumer.addKeywordResult("using"); + + if (SemaRef.getLangOptions().CPlusPlus0x) + Consumer.addKeywordResult("static_assert"); + } + } +} + /// \brief Try to "correct" a typo in the source code by finding /// visible declarations whose names are similar to the name that was /// present in the source code. /// -/// \param Res the \c LookupResult structure that contains the name -/// that was present in the source code along with the name-lookup -/// criteria used to search for the name. On success, this structure -/// will contain the results of name lookup. +/// \param TypoName the \c DeclarationNameInfo structure that contains +/// the name that was present in the source code along with its location. +/// +/// \param LookupKind the name-lookup criteria used to search for the name. /// /// \param S the scope in which name lookup occurs. /// @@ -3181,60 +3567,64 @@ static void LookupPotentialTypoResult(Sema &SemaRef, /// \param OPT when non-NULL, the search for visible declarations will /// also walk the protocols in the qualified interfaces of \p OPT. /// -/// \returns the corrected name if the typo was corrected, otherwise returns an -/// empty \c DeclarationName. When a typo was corrected, the result structure -/// may contain the results of name lookup for the correct name or it may be -/// empty. -DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, - DeclContext *MemberContext, - bool EnteringContext, - CorrectTypoContext CTC, - const ObjCObjectPointerType *OPT) { +/// \returns a \c TypoCorrection containing the corrected name if the typo +/// along with information such as the \c NamedDecl where the corrected name +/// was declared, and any additional \c NestedNameSpecifier needed to access +/// it (C++ only). The \c TypoCorrection is empty if there is no correction. +TypoCorrection Sema::CorrectTypo(const DeclarationNameInfo &TypoName, + Sema::LookupNameKind LookupKind, + Scope *S, CXXScopeSpec *SS, + DeclContext *MemberContext, + bool EnteringContext, + CorrectTypoContext CTC, + const ObjCObjectPointerType *OPT) { if (Diags.hasFatalErrorOccurred() || !getLangOptions().SpellChecking) - return DeclarationName(); + return TypoCorrection(); // We only attempt to correct typos for identifiers. - IdentifierInfo *Typo = Res.getLookupName().getAsIdentifierInfo(); + IdentifierInfo *Typo = TypoName.getName().getAsIdentifierInfo(); if (!Typo) - return DeclarationName(); + return TypoCorrection(); // If the scope specifier itself was invalid, don't try to correct // typos. if (SS && SS->isInvalid()) - return DeclarationName(); + return TypoCorrection(); // Never try to correct typos during template deduction or // instantiation. if (!ActiveTemplateInstantiations.empty()) - return DeclarationName(); + return TypoCorrection(); - TypoCorrectionConsumer Consumer(Typo); + NamespaceSpecifierSet Namespaces(Context, CurContext); + + TypoCorrectionConsumer Consumer(*this, Typo); // Perform name lookup to find visible, similarly-named entities. bool IsUnqualifiedLookup = false; if (MemberContext) { - LookupVisibleDecls(MemberContext, Res.getLookupKind(), Consumer); + LookupVisibleDecls(MemberContext, LookupKind, Consumer); // Look in qualified interfaces. if (OPT) { for (ObjCObjectPointerType::qual_iterator I = OPT->qual_begin(), E = OPT->qual_end(); I != E; ++I) - LookupVisibleDecls(*I, Res.getLookupKind(), Consumer); + LookupVisibleDecls(*I, LookupKind, Consumer); } } else if (SS && SS->isSet()) { DeclContext *DC = computeDeclContext(*SS, EnteringContext); if (!DC) - return DeclarationName(); + return TypoCorrection(); // Provide a stop gap for files that are just seriously broken. Trying // to correct all typos can turn into a HUGE performance penalty, causing // some files to take minutes to get rejected by the parser. if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20) - return DeclarationName(); + return TypoCorrection(); ++TyposCorrected; - LookupVisibleDecls(DC, Res.getLookupKind(), Consumer); + LookupVisibleDecls(DC, LookupKind, Consumer); } else { IsUnqualifiedLookup = true; UnqualifiedTyposCorrectedMap::iterator Cached @@ -3244,7 +3634,7 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, // to correct all typos can turn into a HUGE performance penalty, causing // some files to take minutes to get rejected by the parser. if (TyposCorrected + UnqualifiedTyposCorrected.size() >= 20) - return DeclarationName(); + return TypoCorrection(); // For unqualified lookup, look through all of the names that we have // seen in this translation unit. @@ -3268,313 +3658,225 @@ DeclarationName Sema::CorrectTypo(LookupResult &Res, Scope *S, CXXScopeSpec *SS, } else { // Use the cached value, unless it's a keyword. In the keyword case, we'll // end up adding the keyword below. - if (Cached->second.first.empty()) - return DeclarationName(); + if (!Cached->second) + return TypoCorrection(); - if (!Cached->second.second) - Consumer.FoundName(Cached->second.first); + if (!Cached->second.isKeyword()) + Consumer.addCorrection(Cached->second); } } - // Add context-dependent keywords. - bool WantTypeSpecifiers = false; - bool WantExpressionKeywords = false; - bool WantCXXNamedCasts = false; - bool WantRemainingKeywords = false; - switch (CTC) { - case CTC_Unknown: - WantTypeSpecifiers = true; - WantExpressionKeywords = true; - WantCXXNamedCasts = true; - WantRemainingKeywords = true; - - if (ObjCMethodDecl *Method = getCurMethodDecl()) - if (Method->getClassInterface() && - Method->getClassInterface()->getSuperClass()) - Consumer.addKeywordResult(Context, "super"); - - break; - - case CTC_NoKeywords: - break; - - case CTC_Type: - WantTypeSpecifiers = true; - break; - - case CTC_ObjCMessageReceiver: - Consumer.addKeywordResult(Context, "super"); - // Fall through to handle message receivers like expressions. - - case CTC_Expression: - if (getLangOptions().CPlusPlus) - WantTypeSpecifiers = true; - WantExpressionKeywords = true; - // Fall through to get C++ named casts. - - case CTC_CXXCasts: - WantCXXNamedCasts = true; - break; - - case CTC_ObjCPropertyLookup: - // FIXME: Add "isa"? - break; + AddKeywordsToConsumer(*this, Consumer, S, CTC); - case CTC_MemberLookup: - if (getLangOptions().CPlusPlus) - Consumer.addKeywordResult(Context, "template"); - break; + // If we haven't found anything, we're done. + if (Consumer.empty()) { + // If this was an unqualified lookup, note that no correction was found. + if (IsUnqualifiedLookup) + (void)UnqualifiedTyposCorrected[Typo]; - case CTC_ObjCIvarLookup: - break; + return TypoCorrection(); } - if (WantTypeSpecifiers) { - // Add type-specifier keywords to the set of results. - const char *CTypeSpecs[] = { - "char", "const", "double", "enum", "float", "int", "long", "short", - "signed", "struct", "union", "unsigned", "void", "volatile", "_Bool", - "_Complex", "_Imaginary", - // storage-specifiers as well - "extern", "inline", "static", "typedef" - }; - - const unsigned NumCTypeSpecs = sizeof(CTypeSpecs) / sizeof(CTypeSpecs[0]); - for (unsigned I = 0; I != NumCTypeSpecs; ++I) - Consumer.addKeywordResult(Context, CTypeSpecs[I]); - - if (getLangOptions().C99) - Consumer.addKeywordResult(Context, "restrict"); - if (getLangOptions().Bool || getLangOptions().CPlusPlus) - Consumer.addKeywordResult(Context, "bool"); - - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "class"); - Consumer.addKeywordResult(Context, "typename"); - Consumer.addKeywordResult(Context, "wchar_t"); - - if (getLangOptions().CPlusPlus0x) { - Consumer.addKeywordResult(Context, "char16_t"); - Consumer.addKeywordResult(Context, "char32_t"); - Consumer.addKeywordResult(Context, "constexpr"); - Consumer.addKeywordResult(Context, "decltype"); - Consumer.addKeywordResult(Context, "thread_local"); - } - } + // Make sure that the user typed at least 3 characters for each correction + // made. Otherwise, we don't even both looking at the results. + unsigned ED = Consumer.getBestEditDistance(); + if (ED > 0 && Typo->getName().size() / ED < 3) { + // If this was an unqualified lookup, note that no correction was found. + if (IsUnqualifiedLookup) + (void)UnqualifiedTyposCorrected[Typo]; - if (getLangOptions().GNUMode) - Consumer.addKeywordResult(Context, "typeof"); + return TypoCorrection(); } - if (WantCXXNamedCasts && getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "const_cast"); - Consumer.addKeywordResult(Context, "dynamic_cast"); - Consumer.addKeywordResult(Context, "reinterpret_cast"); - Consumer.addKeywordResult(Context, "static_cast"); + // Build the NestedNameSpecifiers for the KnownNamespaces + if (getLangOptions().CPlusPlus) { + // Load any externally-known namespaces. + if (ExternalSource && !LoadedExternalKnownNamespaces) { + llvm::SmallVector<NamespaceDecl *, 4> ExternalKnownNamespaces; + LoadedExternalKnownNamespaces = true; + ExternalSource->ReadKnownNamespaces(ExternalKnownNamespaces); + for (unsigned I = 0, N = ExternalKnownNamespaces.size(); I != N; ++I) + KnownNamespaces[ExternalKnownNamespaces[I]] = true; + } + + for (llvm::DenseMap<NamespaceDecl*, bool>::iterator + KNI = KnownNamespaces.begin(), + KNIEnd = KnownNamespaces.end(); + KNI != KNIEnd; ++KNI) + Namespaces.AddNamespace(KNI->first); } - if (WantExpressionKeywords) { - Consumer.addKeywordResult(Context, "sizeof"); - if (getLangOptions().Bool || getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "false"); - Consumer.addKeywordResult(Context, "true"); - } + // Weed out any names that could not be found by name lookup. + llvm::SmallPtrSet<IdentifierInfo*, 16> QualifiedResults; + LookupResult TmpRes(*this, TypoName, LookupKind); + TmpRes.suppressDiagnostics(); + while (!Consumer.empty()) { + TypoCorrectionConsumer::distance_iterator DI = Consumer.begin(); + unsigned ED = DI->first; + for (TypoCorrectionConsumer::result_iterator I = DI->second->begin(), + IEnd = DI->second->end(); + I != IEnd; /* Increment in loop. */) { + // If the item already has been looked up or is a keyword, keep it + if (I->second.isResolved()) { + ++I; + continue; + } - if (getLangOptions().CPlusPlus) { - const char *CXXExprs[] = { - "delete", "new", "operator", "throw", "typeid" - }; - const unsigned NumCXXExprs = sizeof(CXXExprs) / sizeof(CXXExprs[0]); - for (unsigned I = 0; I != NumCXXExprs; ++I) - Consumer.addKeywordResult(Context, CXXExprs[I]); + // Perform name lookup on this name. + IdentifierInfo *Name = I->second.getCorrectionAsIdentifierInfo(); + LookupPotentialTypoResult(*this, TmpRes, Name, S, SS, MemberContext, + EnteringContext, CTC); - if (isa<CXXMethodDecl>(CurContext) && - cast<CXXMethodDecl>(CurContext)->isInstance()) - Consumer.addKeywordResult(Context, "this"); + switch (TmpRes.getResultKind()) { + case LookupResult::NotFound: + case LookupResult::NotFoundInCurrentInstantiation: + QualifiedResults.insert(Name); + // We didn't find this name in our scope, or didn't like what we found; + // ignore it. + { + TypoCorrectionConsumer::result_iterator Next = I; + ++Next; + DI->second->erase(I); + I = Next; + } + break; - if (getLangOptions().CPlusPlus0x) { - Consumer.addKeywordResult(Context, "alignof"); - Consumer.addKeywordResult(Context, "nullptr"); + case LookupResult::Ambiguous: + // We don't deal with ambiguities. + return TypoCorrection(); + + case LookupResult::Found: + case LookupResult::FoundOverloaded: + case LookupResult::FoundUnresolvedValue: + I->second.setCorrectionDecl(TmpRes.getAsSingle<NamedDecl>()); + // FIXME: This sets the CorrectionDecl to NULL for overloaded functions. + // It would be nice to find the right one with overload resolution. + ++I; + break; } } - } - - if (WantRemainingKeywords) { - if (getCurFunctionOrMethodDecl() || getCurBlock()) { - // Statements. - const char *CStmts[] = { - "do", "else", "for", "goto", "if", "return", "switch", "while" }; - const unsigned NumCStmts = sizeof(CStmts) / sizeof(CStmts[0]); - for (unsigned I = 0; I != NumCStmts; ++I) - Consumer.addKeywordResult(Context, CStmts[I]); - - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "catch"); - Consumer.addKeywordResult(Context, "try"); - } - if (S && S->getBreakParent()) - Consumer.addKeywordResult(Context, "break"); - - if (S && S->getContinueParent()) - Consumer.addKeywordResult(Context, "continue"); - - if (!getCurFunction()->SwitchStack.empty()) { - Consumer.addKeywordResult(Context, "case"); - Consumer.addKeywordResult(Context, "default"); - } - } else { - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "namespace"); - Consumer.addKeywordResult(Context, "template"); - } + if (DI->second->empty()) + Consumer.erase(DI); + else if (!getLangOptions().CPlusPlus || QualifiedResults.empty() || !ED) + // If there are results in the closest possible bucket, stop + break; - if (S && S->isClassScope()) { - Consumer.addKeywordResult(Context, "explicit"); - Consumer.addKeywordResult(Context, "friend"); - Consumer.addKeywordResult(Context, "mutable"); - Consumer.addKeywordResult(Context, "private"); - Consumer.addKeywordResult(Context, "protected"); - Consumer.addKeywordResult(Context, "public"); - Consumer.addKeywordResult(Context, "virtual"); + // Only perform the qualified lookups for C++ + if (getLangOptions().CPlusPlus) { + TmpRes.suppressDiagnostics(); + for (llvm::SmallPtrSet<IdentifierInfo*, + 16>::iterator QRI = QualifiedResults.begin(), + QRIEnd = QualifiedResults.end(); + QRI != QRIEnd; ++QRI) { + for (NamespaceSpecifierSet::iterator NI = Namespaces.begin(), + NIEnd = Namespaces.end(); + NI != NIEnd; ++NI) { + DeclContext *Ctx = NI->DeclCtx; + unsigned QualifiedED = ED + NI->EditDistance; + + // Stop searching once the namespaces are too far away to create + // acceptable corrections for this identifier (since the namespaces + // are sorted in ascending order by edit distance) + if (QualifiedED > Consumer.getMaxEditDistance()) break; + + TmpRes.clear(); + TmpRes.setLookupName(*QRI); + if (!LookupQualifiedName(TmpRes, Ctx)) continue; + + switch (TmpRes.getResultKind()) { + case LookupResult::Found: + case LookupResult::FoundOverloaded: + case LookupResult::FoundUnresolvedValue: + Consumer.addName((*QRI)->getName(), TmpRes.getAsSingle<NamedDecl>(), + QualifiedED, NI->NameSpecifier); + break; + case LookupResult::NotFound: + case LookupResult::NotFoundInCurrentInstantiation: + case LookupResult::Ambiguous: + break; + } + } } } - if (getLangOptions().CPlusPlus) { - Consumer.addKeywordResult(Context, "using"); - - if (getLangOptions().CPlusPlus0x) - Consumer.addKeywordResult(Context, "static_assert"); - } + QualifiedResults.clear(); } - // If we haven't found anything, we're done. - if (Consumer.empty()) { - // If this was an unqualified lookup, note that no correction was found. - if (IsUnqualifiedLookup) - (void)UnqualifiedTyposCorrected[Typo]; + // No corrections remain... + if (Consumer.empty()) return TypoCorrection(); - return DeclarationName(); - } + TypoResultsMap &BestResults = *Consumer.begin()->second; + ED = Consumer.begin()->first; - // Make sure that the user typed at least 3 characters for each correction - // made. Otherwise, we don't even both looking at the results. - - // We also suppress exact matches; those should be handled by a - // different mechanism (e.g., one that introduces qualification in - // C++). - unsigned ED = Consumer.getBestEditDistance(); if (ED > 0 && Typo->getName().size() / ED < 3) { // If this was an unqualified lookup, note that no correction was found. if (IsUnqualifiedLookup) (void)UnqualifiedTyposCorrected[Typo]; - return DeclarationName(); + return TypoCorrection(); } - // Weed out any names that could not be found by name lookup. - bool LastLookupWasAccepted = false; - for (TypoCorrectionConsumer::iterator I = Consumer.begin(), - IEnd = Consumer.end(); - I != IEnd; /* Increment in loop. */) { - // Keywords are always found. - if (I->second) { - ++I; - continue; - } - - // Perform name lookup on this name. - IdentifierInfo *Name = &Context.Idents.get(I->getKey()); - LookupPotentialTypoResult(*this, Res, Name, S, SS, MemberContext, - EnteringContext, CTC); - - switch (Res.getResultKind()) { - case LookupResult::NotFound: - case LookupResult::NotFoundInCurrentInstantiation: - case LookupResult::Ambiguous: - // We didn't find this name in our scope, or didn't like what we found; - // ignore it. - Res.suppressDiagnostics(); - { - TypoCorrectionConsumer::iterator Next = I; - ++Next; - Consumer.erase(I); - I = Next; - } - LastLookupWasAccepted = false; - break; - - case LookupResult::Found: - case LookupResult::FoundOverloaded: - case LookupResult::FoundUnresolvedValue: - ++I; - LastLookupWasAccepted = true; - break; - } - - if (Res.isAmbiguous()) { - // We don't deal with ambiguities. - Res.suppressDiagnostics(); - Res.clear(); - return DeclarationName(); + // If we have multiple possible corrections, eliminate the ones where we + // added namespace qualifiers to try to resolve the ambiguity (and to favor + // corrections without additional namespace qualifiers) + if (getLangOptions().CPlusPlus && BestResults.size() > 1) { + TypoCorrectionConsumer::distance_iterator DI = Consumer.begin(); + for (TypoCorrectionConsumer::result_iterator I = DI->second->begin(), + IEnd = DI->second->end(); + I != IEnd; /* Increment in loop. */) { + if (I->second.getCorrectionSpecifier() != NULL) { + TypoCorrectionConsumer::result_iterator Cur = I; + ++I; + DI->second->erase(Cur); + } else ++I; } } // If only a single name remains, return that result. - if (Consumer.size() == 1) { - IdentifierInfo *Name = &Context.Idents.get(Consumer.begin()->getKey()); - if (Consumer.begin()->second) { - Res.suppressDiagnostics(); - Res.clear(); - - // Don't correct to a keyword that's the same as the typo; the keyword - // wasn't actually in scope. - if (ED == 0) { - Res.setLookupName(Typo); - return DeclarationName(); - } + if (BestResults.size() == 1) { + const llvm::StringMapEntry<TypoCorrection> &Correction = *(BestResults.begin()); + const TypoCorrection &Result = Correction.second; - } else if (!LastLookupWasAccepted) { - // Perform name lookup on this name. - LookupPotentialTypoResult(*this, Res, Name, S, SS, MemberContext, - EnteringContext, CTC); - } + // Don't correct to a keyword that's the same as the typo; the keyword + // wasn't actually in scope. + if (ED == 0 && Result.isKeyword()) return TypoCorrection(); // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] - = std::make_pair(Name->getName(), Consumer.begin()->second); + UnqualifiedTyposCorrected[Typo] = Result; - return &Context.Idents.get(Consumer.begin()->getKey()); + return Result; } - else if (Consumer.size() > 1 && CTC == CTC_ObjCMessageReceiver - && Consumer["super"]) { - // Prefix 'super' when we're completing in a message-receiver + else if (BestResults.size() > 1 && CTC == CTC_ObjCMessageReceiver + && BestResults["super"].isKeyword()) { + // Prefer 'super' when we're completing in a message-receiver // context. - Res.suppressDiagnostics(); - Res.clear(); // Don't correct to a keyword that's the same as the typo; the keyword // wasn't actually in scope. - if (ED == 0) { - Res.setLookupName(Typo); - return DeclarationName(); - } + if (ED == 0) return TypoCorrection(); // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) - UnqualifiedTyposCorrected[Typo] - = std::make_pair("super", Consumer.begin()->second); + UnqualifiedTyposCorrected[Typo] = BestResults["super"]; - return &Context.Idents.get("super"); + return BestResults["super"]; } - Res.suppressDiagnostics(); - Res.setLookupName(Typo); - Res.clear(); - // Record the correction for unqualified lookup. if (IsUnqualifiedLookup) (void)UnqualifiedTyposCorrected[Typo]; - return DeclarationName(); + return TypoCorrection(); +} + +std::string TypoCorrection::getAsString(const LangOptions &LO) const { + if (CorrectionNameSpec) { + std::string tmpBuffer; + llvm::raw_string_ostream PrefixOStream(tmpBuffer); + CorrectionNameSpec->print(PrefixOStream, PrintingPolicy(LO)); + return PrefixOStream.str() + CorrectionName.getAsString(); + } + + return CorrectionName.getAsString(); } diff --git a/lib/Sema/SemaObjCProperty.cpp b/lib/Sema/SemaObjCProperty.cpp index 6c4469cef9ef..d826ea8d84a4 100644 --- a/lib/Sema/SemaObjCProperty.cpp +++ b/lib/Sema/SemaObjCProperty.cpp @@ -24,6 +24,51 @@ using namespace clang; // Grammar actions. //===----------------------------------------------------------------------===// +/// Check the internal consistency of a property declaration. +static void checkARCPropertyDecl(Sema &S, ObjCPropertyDecl *property) { + if (property->isInvalidDecl()) return; + + ObjCPropertyDecl::PropertyAttributeKind propertyKind + = property->getPropertyAttributes(); + Qualifiers::ObjCLifetime propertyLifetime + = property->getType().getObjCLifetime(); + + // Nothing to do if we don't have a lifetime. + if (propertyLifetime == Qualifiers::OCL_None) return; + + Qualifiers::ObjCLifetime expectedLifetime; + unsigned selector; + + // Strong properties should have either strong or no lifetime. + if (propertyKind & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_copy)) { + expectedLifetime = Qualifiers::OCL_Strong; + selector = 0; + } else if (propertyKind & ObjCPropertyDecl::OBJC_PR_weak) { + expectedLifetime = Qualifiers::OCL_Weak; + selector = 1; + } else if (propertyKind & (ObjCPropertyDecl::OBJC_PR_assign | + ObjCPropertyDecl::OBJC_PR_unsafe_unretained) && + property->getType()->isObjCRetainableType()) { + expectedLifetime = Qualifiers::OCL_ExplicitNone; + selector = 2; + } else { + // We have a lifetime qualifier but no dominating property + // attribute. That's okay. + return; + } + + if (propertyLifetime == expectedLifetime) return; + + property->setInvalidDecl(); + S.Diag(property->getLocation(), + diag::err_arc_inconsistent_property_ownership) + << property->getDeclName() + << selector + << propertyLifetime; +} + Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, FieldDeclarator &FD, ObjCDeclSpec &ODS, @@ -34,6 +79,14 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, tok::ObjCKeywordKind MethodImplKind, DeclContext *lexicalDC) { unsigned Attributes = ODS.getPropertyAttributes(); + TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S); + QualType T = TSI->getType(); + if ((getLangOptions().getGCMode() != LangOptions::NonGC && + T.isObjCGCWeak()) || + (getLangOptions().ObjCAutoRefCount && + T.getObjCLifetime() == Qualifiers::OCL_Weak)) + Attributes |= ObjCDeclSpec::DQ_PR_weak; + bool isReadWrite = ((Attributes & ObjCDeclSpec::DQ_PR_readwrite) || // default is readwrite! !(Attributes & ObjCDeclSpec::DQ_PR_readonly)); @@ -42,9 +95,10 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, bool isAssign = ((Attributes & ObjCDeclSpec::DQ_PR_assign) || (isReadWrite && !(Attributes & ObjCDeclSpec::DQ_PR_retain) && - !(Attributes & ObjCDeclSpec::DQ_PR_copy))); - - TypeSourceInfo *TSI = GetTypeForDeclarator(FD.D, S); + !(Attributes & ObjCDeclSpec::DQ_PR_strong) && + !(Attributes & ObjCDeclSpec::DQ_PR_copy) && + !(Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) && + !(Attributes & ObjCDeclSpec::DQ_PR_weak))); // Proceed with constructing the ObjCPropertDecls. ObjCContainerDecl *ClassDecl = @@ -58,20 +112,27 @@ Decl *Sema::ActOnProperty(Scope *S, SourceLocation AtLoc, Attributes, isOverridingProperty, TSI, MethodImplKind); - if (Res) + if (Res) { CheckObjCPropertyAttributes(Res, AtLoc, Attributes); + if (getLangOptions().ObjCAutoRefCount) + checkARCPropertyDecl(*this, cast<ObjCPropertyDecl>(Res)); + } return Res; } - Decl *Res = CreatePropertyDecl(S, ClassDecl, AtLoc, FD, - GetterSel, SetterSel, - isAssign, isReadWrite, - Attributes, TSI, MethodImplKind); + ObjCPropertyDecl *Res = CreatePropertyDecl(S, ClassDecl, AtLoc, FD, + GetterSel, SetterSel, + isAssign, isReadWrite, + Attributes, TSI, MethodImplKind); if (lexicalDC) Res->setLexicalDeclContext(lexicalDC); // Validate the attributes on the @property. CheckObjCPropertyAttributes(Res, AtLoc, Attributes); + + if (getLangOptions().ObjCAutoRefCount) + checkARCPropertyDecl(*this, Res); + return Res; } @@ -118,6 +179,7 @@ Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, // Set setter/getter selector name. Needed later. PDecl->setGetterName(GetterSel); PDecl->setSetterName(SetterSel); + ProcessDeclAttributes(S, PDecl, FD.D); DC->addDecl(PDecl); // We need to look in the @interface to see if the @property was @@ -139,10 +201,6 @@ Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, CreatePropertyDecl(S, CCPrimary, AtLoc, FD, GetterSel, SetterSel, isAssign, isReadWrite, Attributes, T, MethodImplKind, DC); - // Mark written attribute as having no attribute because - // this is not a user-written property declaration in primary - // class. - PDecl->setPropertyAttributesAsWritten(ObjCPropertyDecl::OBJC_PR_noattr); // A case of continuation class adding a new property in the class. This // is not what it was meant for. However, gcc supports it and so should we. @@ -158,6 +216,7 @@ Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, if (isReadWrite && (PIkind & ObjCPropertyDecl::OBJC_PR_readonly)) { unsigned retainCopyNonatomic = (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | ObjCPropertyDecl::OBJC_PR_copy | ObjCPropertyDecl::OBJC_PR_nonatomic); if ((Attributes & retainCopyNonatomic) != @@ -189,6 +248,8 @@ Sema::HandlePropertyInClassExtension(Scope *S, ObjCCategoryDecl *CDecl, PIDecl->makeitReadWriteAttribute(); if (Attributes & ObjCDeclSpec::DQ_PR_retain) PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); + if (Attributes & ObjCDeclSpec::DQ_PR_strong) + PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); if (Attributes & ObjCDeclSpec::DQ_PR_copy) PIDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); PIDecl->setSetterName(SetterSel); @@ -272,6 +333,35 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, PDecl->setGetterName(GetterSel); PDecl->setSetterName(SetterSel); + unsigned attributesAsWritten = 0; + if (Attributes & ObjCDeclSpec::DQ_PR_readonly) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_readonly; + if (Attributes & ObjCDeclSpec::DQ_PR_readwrite) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_readwrite; + if (Attributes & ObjCDeclSpec::DQ_PR_getter) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_getter; + if (Attributes & ObjCDeclSpec::DQ_PR_setter) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_setter; + if (Attributes & ObjCDeclSpec::DQ_PR_assign) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_assign; + if (Attributes & ObjCDeclSpec::DQ_PR_retain) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_retain; + if (Attributes & ObjCDeclSpec::DQ_PR_strong) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_strong; + if (Attributes & ObjCDeclSpec::DQ_PR_weak) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_weak; + if (Attributes & ObjCDeclSpec::DQ_PR_copy) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_copy; + if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_unsafe_unretained; + if (Attributes & ObjCDeclSpec::DQ_PR_nonatomic) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_nonatomic; + if (Attributes & ObjCDeclSpec::DQ_PR_atomic) + attributesAsWritten |= ObjCPropertyDecl::OBJC_PR_atomic; + + PDecl->setPropertyAttributesAsWritten( + (ObjCPropertyDecl::PropertyAttributeKind)attributesAsWritten); + if (Attributes & ObjCDeclSpec::DQ_PR_readonly) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_readonly); @@ -287,9 +377,18 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, if (Attributes & ObjCDeclSpec::DQ_PR_retain) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_retain); + if (Attributes & ObjCDeclSpec::DQ_PR_strong) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_strong); + + if (Attributes & ObjCDeclSpec::DQ_PR_weak) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_weak); + if (Attributes & ObjCDeclSpec::DQ_PR_copy) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_copy); + if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_unsafe_unretained); + if (isAssign) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign); @@ -298,8 +397,12 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, else if (Attributes & ObjCDeclSpec::DQ_PR_atomic) PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_atomic); - PDecl->setPropertyAttributesAsWritten(PDecl->getPropertyAttributes()); - + // 'unsafe_unretained' is alias for 'assign'. + if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_assign); + if (isAssign) + PDecl->setPropertyAttributes(ObjCPropertyDecl::OBJC_PR_unsafe_unretained); + if (MethodImplKind == tok::objc_required) PDecl->setPropertyImplementation(ObjCPropertyDecl::Required); else if (MethodImplKind == tok::objc_optional) @@ -308,6 +411,93 @@ ObjCPropertyDecl *Sema::CreatePropertyDecl(Scope *S, return PDecl; } +static void checkARCPropertyImpl(Sema &S, SourceLocation propertyImplLoc, + ObjCPropertyDecl *property, + ObjCIvarDecl *ivar) { + if (property->isInvalidDecl() || ivar->isInvalidDecl()) return; + + QualType propertyType = property->getType(); + Qualifiers::ObjCLifetime propertyLifetime = propertyType.getObjCLifetime(); + ObjCPropertyDecl::PropertyAttributeKind propertyKind + = property->getPropertyAttributes(); + + QualType ivarType = ivar->getType(); + Qualifiers::ObjCLifetime ivarLifetime = ivarType.getObjCLifetime(); + + // Case 1: strong properties. + if (propertyLifetime == Qualifiers::OCL_Strong || + (propertyKind & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_copy))) { + switch (ivarLifetime) { + case Qualifiers::OCL_Strong: + // Okay. + return; + + case Qualifiers::OCL_None: + case Qualifiers::OCL_Autoreleasing: + // These aren't valid lifetimes for object ivars; don't diagnose twice. + return; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Weak: + S.Diag(propertyImplLoc, diag::err_arc_strong_property_ownership) + << property->getDeclName() + << ivar->getDeclName() + << ivarLifetime; + break; + } + + // Case 2: weak properties. + } else if (propertyLifetime == Qualifiers::OCL_Weak || + (propertyKind & ObjCPropertyDecl::OBJC_PR_weak)) { + switch (ivarLifetime) { + case Qualifiers::OCL_Weak: + // Okay. + return; + + case Qualifiers::OCL_None: + case Qualifiers::OCL_Autoreleasing: + // These aren't valid lifetimes for object ivars; don't diagnose twice. + return; + + case Qualifiers::OCL_ExplicitNone: + case Qualifiers::OCL_Strong: + S.Diag(propertyImplLoc, diag::error_weak_property) + << property->getDeclName() + << ivar->getDeclName(); + break; + } + + // Case 3: assign properties. + } else if ((propertyKind & ObjCPropertyDecl::OBJC_PR_assign) && + propertyType->isObjCRetainableType()) { + switch (ivarLifetime) { + case Qualifiers::OCL_ExplicitNone: + // Okay. + return; + + case Qualifiers::OCL_None: + case Qualifiers::OCL_Autoreleasing: + // These aren't valid lifetimes for object ivars; don't diagnose twice. + return; + + case Qualifiers::OCL_Weak: + case Qualifiers::OCL_Strong: + S.Diag(propertyImplLoc, diag::err_arc_assign_property_ownership) + << property->getDeclName() + << ivar->getDeclName(); + break; + } + + // Any other property should be ignored. + } else { + return; + } + + S.Diag(property->getLocation(), diag::note_property_declare); +} + /// ActOnPropertyImplDecl - This routine performs semantic checks and /// builds the AST node for a property implementation declaration; declared @@ -396,9 +586,18 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, ObjCIvarDecl *Ivar = 0; // Check that we have a valid, previously declared ivar for @synthesize if (Synthesize) { + if (getLangOptions().ObjCAutoRefCount && + !property->hasWrittenStorageAttribute() && + property->getType()->isObjCRetainableType()) { + Diag(PropertyLoc, diag::err_arc_objc_property_default_assign_on_object); + Diag(property->getLocation(), diag::note_property_declare); + } + // @synthesize if (!PropertyIvar) PropertyIvar = PropertyId; + ObjCPropertyDecl::PropertyAttributeKind kind + = property->getPropertyAttributes(); QualType PropType = Context.getCanonicalType(property->getType()); QualType PropertyIvarType = PropType; if (PropType->isReferenceType()) @@ -407,6 +606,45 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, ObjCInterfaceDecl *ClassDeclared; Ivar = IDecl->lookupInstanceVariable(PropertyIvar, ClassDeclared); if (!Ivar) { + // In ARC, give the ivar a lifetime qualifier based on its + // property attributes. + if (getLangOptions().ObjCAutoRefCount && + !PropertyIvarType.getObjCLifetime()) { + + // retain/copy have retaining lifetime. + if (kind & (ObjCPropertyDecl::OBJC_PR_retain | + ObjCPropertyDecl::OBJC_PR_strong | + ObjCPropertyDecl::OBJC_PR_copy)) { + Qualifiers qs; + qs.addObjCLifetime(Qualifiers::OCL_Strong); + PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs); + } + else if (kind & ObjCPropertyDecl::OBJC_PR_weak) { + if (!getLangOptions().ObjCRuntimeHasWeak) { + Diag(PropertyLoc, diag::err_arc_weak_no_runtime); + Diag(property->getLocation(), diag::note_property_declare); + } + Qualifiers qs; + qs.addObjCLifetime(Qualifiers::OCL_Weak); + PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs); + } + else if (kind & ObjCPropertyDecl::OBJC_PR_assign && + PropertyIvarType->isObjCRetainableType()) { + // assume that an 'assign' property synthesizes __unsafe_unretained + // ivar + Qualifiers qs; + qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone); + PropertyIvarType = Context.getQualifiedType(PropertyIvarType, qs); + } + } + + if (kind & ObjCPropertyDecl::OBJC_PR_weak && + !getLangOptions().ObjCAutoRefCount && + getLangOptions().getGCMode() == LangOptions::NonGC) { + Diag(PropertyLoc, diag::error_synthesize_weak_non_arc_or_gc); + Diag(property->getLocation(), diag::note_property_declare); + } + Ivar = ObjCIvarDecl::Create(Context, ClassImpDecl, PropertyLoc, PropertyLoc, PropertyIvar, PropertyIvarType, /*Dinfo=*/0, @@ -435,7 +673,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, if (PropertyIvarType != IvarType) { bool compat = false; if (isa<ObjCObjectPointerType>(PropertyIvarType) - && isa<ObjCObjectPointerType>(IvarType)) + && isa<ObjCObjectPointerType>(IvarType)) compat = Context.canAssignObjCInterfaces( PropertyIvarType->getAs<ObjCObjectPointerType>(), @@ -470,12 +708,13 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // Fall thru - see previous comment } // __weak is explicit. So it works on Canonical type. - if (PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() && - getLangOptions().getGCMode() != LangOptions::NonGC) { + if ((PropType.isObjCGCWeak() && !IvarType.isObjCGCWeak() && + getLangOptions().getGCMode() != LangOptions::NonGC)) { Diag(PropertyLoc, diag::error_weak_property) << property->getDeclName() << Ivar->getDeclName(); // Fall thru - see previous comment } + // Fall thru - see previous comment if ((property->getType()->isObjCObjectPointerType() || PropType.isObjCGCStrong()) && IvarType.isObjCGCWeak() && getLangOptions().getGCMode() != LangOptions::NonGC) { @@ -484,9 +723,12 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, // Fall thru - see previous comment } } + if (getLangOptions().ObjCAutoRefCount) + checkARCPropertyImpl(*this, PropertyLoc, property, Ivar); } else if (PropertyIvar) // @dynamic Diag(PropertyLoc, diag::error_dynamic_property_ivar_decl); + assert (property && "ActOnPropertyImplDecl - property declaration missing"); ObjCPropertyImplDecl *PIDecl = ObjCPropertyImplDecl::Create(Context, CurContext, AtLoc, PropertyLoc, @@ -523,6 +765,12 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S, PIDecl->setGetterCXXConstructor(ResExpr); } } + if (property->hasAttr<NSReturnsNotRetainedAttr>() && + !getterMethod->hasAttr<NSReturnsNotRetainedAttr>()) { + Diag(getterMethod->getLocation(), + diag::warn_property_getter_owning_mismatch); + Diag(property->getLocation(), diag::note_property_declare); + } } if (ObjCMethodDecl *setterMethod = property->getSetterMethodDecl()) { setterMethod->createImplicitParams(Context, IDecl); @@ -632,10 +880,19 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, != (SAttr & ObjCPropertyDecl::OBJC_PR_copy)) Diag(Property->getLocation(), diag::warn_property_attribute) << Property->getDeclName() << "copy" << inheritedName; - else if ((CAttr & ObjCPropertyDecl::OBJC_PR_retain) - != (SAttr & ObjCPropertyDecl::OBJC_PR_retain)) - Diag(Property->getLocation(), diag::warn_property_attribute) - << Property->getDeclName() << "retain" << inheritedName; + else { + unsigned CAttrRetain = + (CAttr & + (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong)); + unsigned SAttrRetain = + (SAttr & + (ObjCPropertyDecl::OBJC_PR_retain | ObjCPropertyDecl::OBJC_PR_strong)); + bool CStrong = (CAttrRetain != 0); + bool SStrong = (SAttrRetain != 0); + if (CStrong != SStrong) + Diag(Property->getLocation(), diag::warn_property_attribute) + << Property->getDeclName() << "retain (or strong)" << inheritedName; + } if ((CAttr & ObjCPropertyDecl::OBJC_PR_nonatomic) != (SAttr & ObjCPropertyDecl::OBJC_PR_nonatomic)) @@ -653,13 +910,16 @@ Sema::DiagnosePropertyMismatch(ObjCPropertyDecl *Property, QualType RHSType = Context.getCanonicalType(Property->getType()); - if (!Context.typesAreCompatible(LHSType, RHSType)) { - // FIXME: Incorporate this test with typesAreCompatible. - if (LHSType->isObjCQualifiedIdType() && RHSType->isObjCQualifiedIdType()) - if (Context.ObjCQualifiedIdTypesAreCompatible(LHSType, RHSType, false)) - return; - Diag(Property->getLocation(), diag::warn_property_types_are_incompatible) - << Property->getType() << SuperProperty->getType() << inheritedName; + if (!Context.propertyTypesAreCompatible(LHSType, RHSType)) { + // Do cases not handled in above. + // FIXME. For future support of covariant property types, revisit this. + bool IncompatibleObjC = false; + QualType ConvertedType; + if (!isObjCPointerConversion(RHSType, LHSType, + ConvertedType, IncompatibleObjC) || + IncompatibleObjC) + Diag(Property->getLocation(), diag::warn_property_types_are_incompatible) + << Property->getType() << SuperProperty->getType() << inheritedName; } } @@ -1050,7 +1310,7 @@ void Sema::DiagnoseUnimplementedProperties(Scope *S, ObjCImplDecl* IMPDecl, // Is there a matching propery synthesize/dynamic? if (Prop->isInvalidDecl() || Prop->getPropertyImplementation() == ObjCPropertyDecl::Optional || - PropImplMap.count(Prop)) + PropImplMap.count(Prop) || Prop->hasAttr<UnavailableAttr>()) continue; if (!InsMap.count(Prop->getGetterName())) { Diag(Prop->getLocation(), @@ -1135,6 +1395,35 @@ Sema::AtomicPropertySetterGetterRules (ObjCImplDecl* IMPDecl, } } +void Sema::DiagnoseOwningPropertyGetterSynthesis(const ObjCImplementationDecl *D) { + if (getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + + for (ObjCImplementationDecl::propimpl_iterator + i = D->propimpl_begin(), e = D->propimpl_end(); i != e; ++i) { + ObjCPropertyImplDecl *PID = *i; + if (PID->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + + const ObjCPropertyDecl *PD = PID->getPropertyDecl(); + if (PD && !PD->hasAttr<NSReturnsNotRetainedAttr>() && + !D->getInstanceMethod(PD->getGetterName())) { + ObjCMethodDecl *method = PD->getGetterMethodDecl(); + if (!method) + continue; + ObjCMethodFamily family = method->getMethodFamily(); + if (family == OMF_alloc || family == OMF_copy || + family == OMF_mutableCopy || family == OMF_new) { + if (getLangOptions().ObjCAutoRefCount) + Diag(PID->getLocation(), diag::err_ownin_getter_rule); + else + Diag(PID->getLocation(), diag::warn_ownin_getter_rule); + Diag(PD->getLocation(), diag::note_property_declare); + } + } + } +} + /// AddPropertyAttrs - Propagates attributes from a property to the /// implicitly-declared getter or setter for that property. static void AddPropertyAttrs(Sema &S, ObjCMethodDecl *PropertyMethod, @@ -1214,6 +1503,9 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, // and the real context should be the same. if (lexicalDC) GetterMethod->setLexicalDeclContext(lexicalDC); + if (property->hasAttr<NSReturnsNotRetainedAttr>()) + GetterMethod->addAttr( + ::new (Context) NSReturnsNotRetainedAttr(Loc, Context)); } else // A user declared getter will be synthesize when @synthesize of // the property with the same name is seen in the @implementation @@ -1245,7 +1537,7 @@ void Sema::ProcessPropertyDecl(ObjCPropertyDecl *property, ParmVarDecl *Argument = ParmVarDecl::Create(Context, SetterMethod, Loc, Loc, property->getIdentifier(), - property->getType(), + property->getType().getUnqualifiedType(), /*TInfo=*/0, SC_None, SC_None, @@ -1287,7 +1579,7 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, SourceLocation Loc, unsigned &Attributes) { // FIXME: Improve the reported location. - if (!PDecl) + if (!PDecl || PDecl->isInvalidDecl()) return; ObjCPropertyDecl *PropertyDecl = cast<ObjCPropertyDecl>(PDecl); @@ -1297,12 +1589,16 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, if ((Attributes & ObjCDeclSpec::DQ_PR_readonly) && (Attributes & (ObjCDeclSpec::DQ_PR_readwrite | ObjCDeclSpec::DQ_PR_assign | + ObjCDeclSpec::DQ_PR_unsafe_unretained | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain))) { + ObjCDeclSpec::DQ_PR_retain | + ObjCDeclSpec::DQ_PR_strong))) { const char * which = (Attributes & ObjCDeclSpec::DQ_PR_readwrite) ? "readwrite" : (Attributes & ObjCDeclSpec::DQ_PR_assign) ? "assign" : + (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) ? + "unsafe_unretained" : (Attributes & ObjCDeclSpec::DQ_PR_copy) ? "copy" : "retain"; @@ -1313,14 +1609,15 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, } // Check for copy or retain on non-object types. - if ((Attributes & (ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain)) && - !PropertyTy->isObjCObjectPointerType() && - !PropertyTy->isBlockPointerType() && - !Context.isObjCNSObjectType(PropertyTy) && + if ((Attributes & (ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy | + ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong)) && + !PropertyTy->isObjCRetainableType() && !PropertyDecl->getAttr<ObjCNSObjectAttr>()) { Diag(Loc, diag::err_objc_property_requires_object) - << (Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain"); - Attributes &= ~(ObjCDeclSpec::DQ_PR_copy | ObjCDeclSpec::DQ_PR_retain); + << (Attributes & ObjCDeclSpec::DQ_PR_weak ? "weak" : + Attributes & ObjCDeclSpec::DQ_PR_copy ? "copy" : "retain (or strong)"); + Attributes &= ~(ObjCDeclSpec::DQ_PR_weak | ObjCDeclSpec::DQ_PR_copy | + ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong); } // Check for more than one of { assign, copy, retain }. @@ -1335,18 +1632,75 @@ void Sema::CheckObjCPropertyAttributes(Decl *PDecl, << "assign" << "retain"; Attributes &= ~ObjCDeclSpec::DQ_PR_retain; } + if (Attributes & ObjCDeclSpec::DQ_PR_strong) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "assign" << "strong"; + Attributes &= ~ObjCDeclSpec::DQ_PR_strong; + } + if (getLangOptions().ObjCAutoRefCount && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "assign" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } + } else if (Attributes & ObjCDeclSpec::DQ_PR_unsafe_unretained) { + if (Attributes & ObjCDeclSpec::DQ_PR_copy) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "copy"; + Attributes &= ~ObjCDeclSpec::DQ_PR_copy; + } + if (Attributes & ObjCDeclSpec::DQ_PR_retain) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "retain"; + Attributes &= ~ObjCDeclSpec::DQ_PR_retain; + } + if (Attributes & ObjCDeclSpec::DQ_PR_strong) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "strong"; + Attributes &= ~ObjCDeclSpec::DQ_PR_strong; + } + if (getLangOptions().ObjCAutoRefCount && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "unsafe_unretained" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } } else if (Attributes & ObjCDeclSpec::DQ_PR_copy) { if (Attributes & ObjCDeclSpec::DQ_PR_retain) { Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) << "copy" << "retain"; Attributes &= ~ObjCDeclSpec::DQ_PR_retain; } + if (Attributes & ObjCDeclSpec::DQ_PR_strong) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "copy" << "strong"; + Attributes &= ~ObjCDeclSpec::DQ_PR_strong; + } + if (Attributes & ObjCDeclSpec::DQ_PR_weak) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "copy" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } + } + else if ((Attributes & ObjCDeclSpec::DQ_PR_retain) && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "retain" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; + } + else if ((Attributes & ObjCDeclSpec::DQ_PR_strong) && + (Attributes & ObjCDeclSpec::DQ_PR_weak)) { + Diag(Loc, diag::err_objc_property_attr_mutually_exclusive) + << "strong" << "weak"; + Attributes &= ~ObjCDeclSpec::DQ_PR_weak; } // Warn if user supplied no assignment attribute, property is // readwrite, and this is an object type. if (!(Attributes & (ObjCDeclSpec::DQ_PR_assign | ObjCDeclSpec::DQ_PR_copy | - ObjCDeclSpec::DQ_PR_retain)) && + ObjCDeclSpec::DQ_PR_unsafe_unretained | + ObjCDeclSpec::DQ_PR_retain | ObjCDeclSpec::DQ_PR_strong | + ObjCDeclSpec::DQ_PR_weak)) && !(Attributes & ObjCDeclSpec::DQ_PR_readonly) && PropertyTy->isObjCObjectPointerType()) { // Skip this warning in gc-only mode. diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 4bba6f8877bf..437b2b5c6d43 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -38,8 +38,10 @@ using namespace sema; /// function. static ExprResult CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, - SourceLocation Loc = SourceLocation()) { - ExprResult E = S.Owned(new (S.Context) DeclRefExpr(Fn, Fn->getType(), VK_LValue, Loc)); + SourceLocation Loc = SourceLocation(), + const DeclarationNameLoc &LocInfo = DeclarationNameLoc()){ + ExprResult E = S.Owned(new (S.Context) DeclRefExpr(Fn, Fn->getType(), + VK_LValue, Loc, LocInfo)); E = S.DefaultFunctionArrayConversion(E.take()); if (E.isInvalid()) return ExprError(); @@ -49,7 +51,8 @@ CreateFunctionRefExpr(Sema &S, FunctionDecl *Fn, static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, bool InOverloadResolution, StandardConversionSequence &SCS, - bool CStyle); + bool CStyle, + bool AllowObjCWritebackConversion); static bool IsTransparentUnionStandardConversion(Sema &S, Expr* From, QualType &ToType, @@ -106,6 +109,7 @@ GetConversionCategory(ImplicitConversionKind Kind) { ICC_Conversion, ICC_Conversion, ICC_Conversion, + ICC_Conversion, ICC_Conversion }; return Category[(int)Kind]; @@ -138,7 +142,8 @@ ImplicitConversionRank GetConversionRank(ImplicitConversionKind Kind) { ICR_Conversion, ICR_Complex_Real_Conversion, ICR_Conversion, - ICR_Conversion + ICR_Conversion, + ICR_Writeback_Conversion }; return Rank[(int)Kind]; } @@ -170,6 +175,7 @@ const char* GetImplicitConversionName(ImplicitConversionKind Kind) { "Complex-real conversion", "Block Pointer conversion", "Transparent Union Conversion" + "Writeback conversion" }; return Name[Kind]; } @@ -181,12 +187,14 @@ void StandardConversionSequence::setAsIdentityConversion() { Second = ICK_Identity; Third = ICK_Identity; DeprecatedStringLiteralToCharPtr = false; + QualificationIncludesObjCLifetime = false; ReferenceBinding = false; DirectBinding = false; IsLvalueReference = true; BindsToFunctionLvalue = false; BindsToRvalue = false; BindsImplicitObjectArgumentWithoutRefQualifier = false; + ObjCLifetimeConversionBinding = false; CopyConstructor = 0; } @@ -730,6 +738,15 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, return false; } +/// \brief Checks availability of the function depending on the current +/// function context. Inside an unavailable function, unavailability is ignored. +/// +/// \returns true if \arg FD is unavailable and current context is inside +/// an available function, false otherwise. +bool Sema::isFunctionConsideredUnavailable(FunctionDecl *FD) { + return FD->isUnavailable() && !cast<Decl>(CurContext)->isUnavailable(); +} + /// TryImplicitConversion - Attempt to perform an implicit conversion /// from the given expression (Expr) to the given type (ToType). This /// function returns an implicit conversion sequence that can be used @@ -753,15 +770,20 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old, /// not permitted. /// If @p AllowExplicit, then explicit user-defined conversions are /// permitted. +/// +/// \param AllowObjCWritebackConversion Whether we allow the Objective-C +/// writeback conversion, which allows __autoreleasing id* parameters to +/// be initialized with __strong id* or __weak id* arguments. static ImplicitConversionSequence TryImplicitConversion(Sema &S, Expr *From, QualType ToType, bool SuppressUserConversions, bool AllowExplicit, bool InOverloadResolution, - bool CStyle) { + bool CStyle, + bool AllowObjCWritebackConversion) { ImplicitConversionSequence ICS; if (IsStandardConversion(S, From, ToType, InOverloadResolution, - ICS.Standard, CStyle)) { + ICS.Standard, CStyle, AllowObjCWritebackConversion)){ ICS.setStandard(); return ICS; } @@ -867,24 +889,17 @@ TryImplicitConversion(Sema &S, Expr *From, QualType ToType, return ICS; } -bool Sema::TryImplicitConversion(InitializationSequence &Sequence, - const InitializedEntity &Entity, - Expr *Initializer, - bool SuppressUserConversions, - bool AllowExplicitConversions, - bool InOverloadResolution, - bool CStyle) { - ImplicitConversionSequence ICS - = clang::TryImplicitConversion(*this, Initializer, Entity.getType(), - SuppressUserConversions, - AllowExplicitConversions, - InOverloadResolution, - CStyle); - if (ICS.isBad()) return true; - - // Perform the actual conversion. - Sequence.AddConversionSequenceStep(ICS, Entity.getType()); - return false; +ImplicitConversionSequence +Sema::TryImplicitConversion(Expr *From, QualType ToType, + bool SuppressUserConversions, + bool AllowExplicit, + bool InOverloadResolution, + bool CStyle, + bool AllowObjCWritebackConversion) { + return clang::TryImplicitConversion(*this, From, ToType, + SuppressUserConversions, AllowExplicit, + InOverloadResolution, CStyle, + AllowObjCWritebackConversion); } /// PerformImplicitConversion - Perform an implicit conversion of the @@ -903,18 +918,25 @@ ExprResult Sema::PerformImplicitConversion(Expr *From, QualType ToType, AssignmentAction Action, bool AllowExplicit, ImplicitConversionSequence& ICS) { + // Objective-C ARC: Determine whether we will allow the writeback conversion. + bool AllowObjCWritebackConversion + = getLangOptions().ObjCAutoRefCount && + (Action == AA_Passing || Action == AA_Sending); + + ICS = clang::TryImplicitConversion(*this, From, ToType, /*SuppressUserConversions=*/false, AllowExplicit, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + AllowObjCWritebackConversion); return PerformImplicitConversion(From, ToType, ICS, Action); } /// \brief Determine whether the conversion from FromType to ToType is a valid /// conversion that strips "noreturn" off the nested function type. -static bool IsNoReturnConversion(ASTContext &Context, QualType FromType, - QualType ToType, QualType &ResultTy) { +bool Sema::IsNoReturnConversion(QualType FromType, QualType ToType, + QualType &ResultTy) { if (Context.hasSameUnqualifiedType(FromType, ToType)) return false; @@ -1016,7 +1038,8 @@ static bool IsVectorConversion(ASTContext &Context, QualType FromType, static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, bool InOverloadResolution, StandardConversionSequence &SCS, - bool CStyle) { + bool CStyle, + bool AllowObjCWritebackConversion) { QualType FromType = From->getType(); // Standard conversions (C++ [conv]) @@ -1054,7 +1077,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, S.ExtractUnqualifiedFunctionType(ToType), FromType)) { QualType resultTy; // if the function type matches except for [[noreturn]], it's ok - if (!IsNoReturnConversion(S.Context, FromType, + if (!S.IsNoReturnConversion(FromType, S.ExtractUnqualifiedFunctionType(ToType), resultTy)) // otherwise, only a boolean conversion is standard if (!ToType->isBooleanType()) @@ -1123,6 +1146,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // conversion (4.4). (C++ 4.2p2) SCS.Second = ICK_Identity; SCS.Third = ICK_Qualification; + SCS.QualificationIncludesObjCLifetime = false; SCS.setAllToTypes(FromType); return true; } @@ -1199,7 +1223,10 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, SCS.Second = ICK_Floating_Integral; FromType = ToType.getUnqualifiedType(); } else if (S.IsBlockPointerConversion(FromType, ToType, FromType)) { - SCS.Second = ICK_Block_Pointer_Conversion; + SCS.Second = ICK_Block_Pointer_Conversion; + } else if (AllowObjCWritebackConversion && + S.isObjCWritebackConversion(FromType, ToType, FromType)) { + SCS.Second = ICK_Writeback_Conversion; } else if (S.IsPointerConversion(From, FromType, ToType, InOverloadResolution, FromType, IncompatibleObjC)) { // Pointer conversions (C++ 4.10). @@ -1218,7 +1245,7 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, // Compatible conversions (Clang extension for C function overloading) SCS.Second = ICK_Compatible_Conversion; FromType = ToType.getUnqualifiedType(); - } else if (IsNoReturnConversion(S.Context, FromType, ToType, FromType)) { + } else if (S.IsNoReturnConversion(FromType, ToType, FromType)) { // Treat a conversion that strips "noreturn" as an identity conversion. SCS.Second = ICK_NoReturn_Adjustment; } else if (IsTransparentUnionStandardConversion(S, From, ToType, @@ -1235,8 +1262,11 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, QualType CanonFrom; QualType CanonTo; // The third conversion can be a qualification conversion (C++ 4p1). - if (S.IsQualificationConversion(FromType, ToType, CStyle)) { + bool ObjCLifetimeConversion; + if (S.IsQualificationConversion(FromType, ToType, CStyle, + ObjCLifetimeConversion)) { SCS.Third = ICK_Qualification; + SCS.QualificationIncludesObjCLifetime = ObjCLifetimeConversion; FromType = ToType; CanonFrom = S.Context.getCanonicalType(FromType); CanonTo = S.Context.getCanonicalType(ToType); @@ -1253,7 +1283,8 @@ static bool IsStandardConversion(Sema &S, Expr* From, QualType ToType, if (CanonFrom.getLocalUnqualifiedType() == CanonTo.getLocalUnqualifiedType() && (CanonFrom.getLocalCVRQualifiers() != CanonTo.getLocalCVRQualifiers() - || CanonFrom.getObjCGCAttr() != CanonTo.getObjCGCAttr())) { + || CanonFrom.getObjCGCAttr() != CanonTo.getObjCGCAttr() + || CanonFrom.getObjCLifetime() != CanonTo.getObjCLifetime())) { FromType = ToType; CanonFrom = CanonTo; } @@ -1284,7 +1315,8 @@ IsTransparentUnionStandardConversion(Sema &S, Expr* From, for (RecordDecl::field_iterator it = UD->field_begin(), itend = UD->field_end(); it != itend; ++it) { - if (IsStandardConversion(S, From, it->getType(), InOverloadResolution, SCS, CStyle)) { + if (IsStandardConversion(S, From, it->getType(), InOverloadResolution, SCS, + CStyle, /*ObjCWritebackConversion=*/false)) { ToType = it->getType(); return true; } @@ -1479,16 +1511,18 @@ bool Sema::IsComplexPromotion(QualType FromType, QualType ToType) { /// same type qualifiers as FromPtr has on its pointee type. ToType, /// if non-empty, will be a pointer to ToType that may or may not have /// the right set of qualifiers on its pointee. +/// static QualType BuildSimilarlyQualifiedPointerType(const Type *FromPtr, QualType ToPointee, QualType ToType, - ASTContext &Context) { + ASTContext &Context, + bool StripObjCLifetime = false) { assert((FromPtr->getTypeClass() == Type::Pointer || FromPtr->getTypeClass() == Type::ObjCObjectPointer) && "Invalid similarly-qualified pointer type"); - /// \brief Conversions to 'id' subsume cv-qualifier conversions. - if (ToType->isObjCIdType() || ToType->isObjCQualifiedIdType()) + /// Conversions to 'id' subsume cv-qualifier conversions. + if (ToType->isObjCIdType() || ToType->isObjCQualifiedIdType()) return ToType.getUnqualifiedType(); QualType CanonFromPointee @@ -1496,6 +1530,9 @@ BuildSimilarlyQualifiedPointerType(const Type *FromPtr, QualType CanonToPointee = Context.getCanonicalType(ToPointee); Qualifiers Quals = CanonFromPointee.getQualifiers(); + if (StripObjCLifetime) + Quals.removeObjCLifetime(); + // Exact qualifier match -> return the pointer type we're converting to. if (CanonToPointee.getLocalQualifiers() == Quals) { // ToType is exactly what we need. Return it. @@ -1599,7 +1636,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, // Beyond this point, both types need to be pointers // , including objective-c pointers. QualType ToPointeeType = ToTypePtr->getPointeeType(); - if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType()) { + if (FromType->isObjCObjectPointerType() && ToPointeeType->isVoidType() && + !getLangOptions().ObjCAutoRefCount) { ConvertedType = BuildSimilarlyQualifiedPointerType( FromType->getAs<ObjCObjectPointerType>(), ToPointeeType, @@ -1624,7 +1662,8 @@ bool Sema::IsPointerConversion(Expr *From, QualType FromType, QualType ToType, ToPointeeType->isVoidType()) { ConvertedType = BuildSimilarlyQualifiedPointerType(FromTypePtr, ToPointeeType, - ToType, Context); + ToType, Context, + /*StripObjCLifetime=*/true); return true; } @@ -1814,6 +1853,7 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, ToPointeeType->getAs<ObjCObjectPointerType>() && isObjCPointerConversion(FromPointeeType, ToPointeeType, ConvertedType, IncompatibleObjC)) { + ConvertedType = Context.getPointerType(ConvertedType); ConvertedType = AdoptQualifiers(Context, ConvertedType, FromQualifiers); return true; @@ -1885,6 +1925,73 @@ bool Sema::isObjCPointerConversion(QualType FromType, QualType ToType, return false; } +/// \brief Determine whether this is an Objective-C writeback conversion, +/// used for parameter passing when performing automatic reference counting. +/// +/// \param FromType The type we're converting form. +/// +/// \param ToType The type we're converting to. +/// +/// \param ConvertedType The type that will be produced after applying +/// this conversion. +bool Sema::isObjCWritebackConversion(QualType FromType, QualType ToType, + QualType &ConvertedType) { + if (!getLangOptions().ObjCAutoRefCount || + Context.hasSameUnqualifiedType(FromType, ToType)) + return false; + + // Parameter must be a pointer to __autoreleasing (with no other qualifiers). + QualType ToPointee; + if (const PointerType *ToPointer = ToType->getAs<PointerType>()) + ToPointee = ToPointer->getPointeeType(); + else + return false; + + Qualifiers ToQuals = ToPointee.getQualifiers(); + if (!ToPointee->isObjCLifetimeType() || + ToQuals.getObjCLifetime() != Qualifiers::OCL_Autoreleasing || + !ToQuals.withoutObjCGLifetime().empty()) + return false; + + // Argument must be a pointer to __strong to __weak. + QualType FromPointee; + if (const PointerType *FromPointer = FromType->getAs<PointerType>()) + FromPointee = FromPointer->getPointeeType(); + else + return false; + + Qualifiers FromQuals = FromPointee.getQualifiers(); + if (!FromPointee->isObjCLifetimeType() || + (FromQuals.getObjCLifetime() != Qualifiers::OCL_Strong && + FromQuals.getObjCLifetime() != Qualifiers::OCL_Weak)) + return false; + + // Make sure that we have compatible qualifiers. + FromQuals.setObjCLifetime(Qualifiers::OCL_Autoreleasing); + if (!ToQuals.compatiblyIncludes(FromQuals)) + return false; + + // Remove qualifiers from the pointee type we're converting from; they + // aren't used in the compatibility check belong, and we'll be adding back + // qualifiers (with __autoreleasing) if the compatibility check succeeds. + FromPointee = FromPointee.getUnqualifiedType(); + + // The unqualified form of the pointee types must be compatible. + ToPointee = ToPointee.getUnqualifiedType(); + bool IncompatibleObjC; + if (Context.typesAreCompatible(FromPointee, ToPointee)) + FromPointee = ToPointee; + else if (!isObjCPointerConversion(FromPointee, ToPointee, FromPointee, + IncompatibleObjC)) + return false; + + /// \brief Construct the type we're converting to, which is a pointer to + /// __autoreleasing pointee. + FromPointee = Context.getQualifiedType(FromPointee, FromQuals); + ConvertedType = Context.getPointerType(FromPointee); + return true; +} + bool Sema::IsBlockPointerConversion(QualType FromType, QualType ToType, QualType& ConvertedType) { QualType ToPointeeType; @@ -2178,12 +2285,17 @@ bool Sema::CheckMemberPointerConversion(Expr *From, QualType ToType, /// IsQualificationConversion - Determines whether the conversion from /// an rvalue of type FromType to ToType is a qualification conversion /// (C++ 4.4). +/// +/// \param ObjCLifetimeConversion Output parameter that will be set to indicate +/// when the qualification conversion involves a change in the Objective-C +/// object lifetime. bool Sema::IsQualificationConversion(QualType FromType, QualType ToType, - bool CStyle) { + bool CStyle, bool &ObjCLifetimeConversion) { FromType = Context.getCanonicalType(FromType); ToType = Context.getCanonicalType(ToType); - + ObjCLifetimeConversion = false; + // If FromType and ToType are the same type, this is not a // qualification conversion. if (FromType.getUnqualifiedType() == ToType.getUnqualifiedType()) @@ -2206,6 +2318,21 @@ Sema::IsQualificationConversion(QualType FromType, QualType ToType, Qualifiers FromQuals = FromType.getQualifiers(); Qualifiers ToQuals = ToType.getQualifiers(); + // Objective-C ARC: + // Check Objective-C lifetime conversions. + if (FromQuals.getObjCLifetime() != ToQuals.getObjCLifetime() && + UnwrappedAnyPointer) { + if (ToQuals.compatiblyIncludesObjCLifetime(FromQuals)) { + ObjCLifetimeConversion = true; + FromQuals.removeObjCLifetime(); + ToQuals.removeObjCLifetime(); + } else { + // Qualification conversions cannot cast between different + // Objective-C lifetime qualifiers. + return false; + } + } + // Allow addition/removal of GC attributes but not changing GC attributes. if (FromQuals.getObjCGCAttr() != ToQuals.getObjCGCAttr() && (!FromQuals.hasObjCGCAttr() || !ToQuals.hasObjCGCAttr())) { @@ -2713,6 +2840,15 @@ CompareStandardConversionSequences(Sema &S, QualType UnqualT1 = S.Context.getUnqualifiedArrayType(T1, T1Quals); QualType UnqualT2 = S.Context.getUnqualifiedArrayType(T2, T2Quals); if (UnqualT1 == UnqualT2) { + // Objective-C++ ARC: If the references refer to objects with different + // lifetimes, prefer bindings that don't change lifetime. + if (SCS1.ObjCLifetimeConversionBinding != + SCS2.ObjCLifetimeConversionBinding) { + return SCS1.ObjCLifetimeConversionBinding + ? ImplicitConversionSequence::Worse + : ImplicitConversionSequence::Better; + } + // If the type is an array type, promote the element qualifiers to the // type for comparison. if (isa<ArrayType>(T1) && T1Quals) @@ -2722,7 +2858,7 @@ CompareStandardConversionSequences(Sema &S, if (T2.isMoreQualifiedThan(T1)) return ImplicitConversionSequence::Better; else if (T1.isMoreQualifiedThan(T2)) - return ImplicitConversionSequence::Worse; + return ImplicitConversionSequence::Worse; } } @@ -2770,6 +2906,17 @@ CompareQualificationConversions(Sema &S, ImplicitConversionSequence::CompareKind Result = ImplicitConversionSequence::Indistinguishable; + + // Objective-C++ ARC: + // Prefer qualification conversions not involving a change in lifetime + // to qualification conversions that do not change lifetime. + if (SCS1.QualificationIncludesObjCLifetime != + SCS2.QualificationIncludesObjCLifetime) { + Result = SCS1.QualificationIncludesObjCLifetime + ? ImplicitConversionSequence::Worse + : ImplicitConversionSequence::Better; + } + while (S.Context.UnwrapSimilarPointerTypes(T1, T2)) { // Within each iteration of the loop, we check the qualifiers to // determine if this still looks like a qualification @@ -3039,7 +3186,8 @@ Sema::ReferenceCompareResult Sema::CompareReferenceRelationship(SourceLocation Loc, QualType OrigT1, QualType OrigT2, bool &DerivedToBase, - bool &ObjCConversion) { + bool &ObjCConversion, + bool &ObjCLifetimeConversion) { assert(!OrigT1->isReferenceType() && "T1 must be the pointee type of the reference type"); assert(!OrigT2->isReferenceType() && "T2 cannot be a reference type"); @@ -3056,6 +3204,7 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, // T1 is a base class of T2. DerivedToBase = false; ObjCConversion = false; + ObjCLifetimeConversion = false; if (UnqualT1 == UnqualT2) { // Nothing to do. } else if (!RequireCompleteType(Loc, OrigT2, PDiag()) && @@ -3090,9 +3239,16 @@ Sema::CompareReferenceRelationship(SourceLocation Loc, // qualifiers when performing these computations, so that e.g., an int in // address space 1 is not reference-compatible with an int in address // space 2. + if (T1Quals.getObjCLifetime() != T2Quals.getObjCLifetime() && + T1Quals.compatiblyIncludesObjCLifetime(T2Quals)) { + T1Quals.removeObjCLifetime(); + T2Quals.removeObjCLifetime(); + ObjCLifetimeConversion = true; + } + if (T1Quals == T2Quals) return Ref_Compatible; - else if (T1.isMoreQualifiedThan(T2)) + else if (T1Quals.compatiblyIncludes(T2Quals)) return Ref_Compatible_With_Added_Qualification; else return Ref_Related; @@ -3135,13 +3291,14 @@ FindConversionForRefInit(Sema &S, ImplicitConversionSequence &ICS, if (AllowRvalues) { bool DerivedToBase = false; bool ObjCConversion = false; + bool ObjCLifetimeConversion = false; if (!ConvTemplate && S.CompareReferenceRelationship( DeclLoc, Conv->getConversionType().getNonReferenceType() .getUnqualifiedType(), DeclType.getNonReferenceType().getUnqualifiedType(), - DerivedToBase, ObjCConversion) == + DerivedToBase, ObjCConversion, ObjCLifetimeConversion) == Sema::Ref_Incompatible) continue; } else { @@ -3242,10 +3399,11 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, bool isRValRef = DeclType->isRValueReferenceType(); bool DerivedToBase = false; bool ObjCConversion = false; + bool ObjCLifetimeConversion = false; Expr::Classification InitCategory = Init->Classify(S.Context); Sema::ReferenceCompareResult RefRelationship = S.CompareReferenceRelationship(DeclLoc, T1, T2, DerivedToBase, - ObjCConversion); + ObjCConversion, ObjCLifetimeConversion); // C++0x [dcl.init.ref]p5: @@ -3283,6 +3441,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = false; ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = ObjCLifetimeConversion; ICS.Standard.CopyConstructor = 0; // Nothing more to do: the inaccessibility/ambiguity check for @@ -3328,7 +3487,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, // -- If the initializer expression // // -- is an xvalue, class prvalue, array prvalue or function - // lvalue and "cv1T1" is reference-compatible with "cv2 T2", or + // lvalue and "cv1 T1" is reference-compatible with "cv2 T2", or if (RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification && (InitCategory.isXValue() || (InitCategory.isPRValue() && (T2->isRecordType() || T2->isArrayType())) || @@ -3356,6 +3515,7 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = InitCategory.isRValue(); ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = ObjCLifetimeConversion; ICS.Standard.CopyConstructor = 0; return ICS; } @@ -3398,7 +3558,17 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, // we would be reference-compatible or reference-compatible with // added qualification. But that wasn't the case, so the reference // initialization fails. - return ICS; + // + // Note that we only want to check address spaces and cvr-qualifiers here. + // ObjC GC and lifetime qualifiers aren't important. + Qualifiers T1Quals = T1.getQualifiers(); + Qualifiers T2Quals = T2.getQualifiers(); + T1Quals.removeObjCGCAttr(); + T1Quals.removeObjCLifetime(); + T2Quals.removeObjCGCAttr(); + T2Quals.removeObjCLifetime(); + if (!T1Quals.compatiblyIncludes(T2Quals)) + return ICS; } // If at least one of the types is a class type, the types are not @@ -3429,7 +3599,8 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS = TryImplicitConversion(S, Init, T1, SuppressUserConversions, /*AllowExplicit=*/false, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); // Of course, that's still a reference binding. if (ICS.isStandard()) { @@ -3438,12 +3609,14 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = true; ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = false; } else if (ICS.isUserDefined()) { ICS.UserDefined.After.ReferenceBinding = true; ICS.Standard.IsLvalueReference = !isRValRef; ICS.Standard.BindsToFunctionLvalue = T2->isFunctionType(); ICS.Standard.BindsToRvalue = true; ICS.Standard.BindsImplicitObjectArgumentWithoutRefQualifier = false; + ICS.Standard.ObjCLifetimeConversionBinding = false; } return ICS; @@ -3458,7 +3631,8 @@ TryReferenceInit(Sema &S, Expr *&Init, QualType DeclType, static ImplicitConversionSequence TryCopyInitialization(Sema &S, Expr *From, QualType ToType, bool SuppressUserConversions, - bool InOverloadResolution) { + bool InOverloadResolution, + bool AllowObjCWritebackConversion) { if (ToType->isReferenceType()) return TryReferenceInit(S, From, ToType, /*FIXME:*/From->getLocStart(), @@ -3469,7 +3643,8 @@ TryCopyInitialization(Sema &S, Expr *From, QualType ToType, SuppressUserConversions, /*AllowExplicit=*/false, InOverloadResolution, - /*CStyle=*/false); + /*CStyle=*/false, + AllowObjCWritebackConversion); } /// TryObjectArgumentInitialization - Try to initialize the object @@ -3659,7 +3834,8 @@ TryContextuallyConvertToBool(Sema &S, Expr *From) { /*SuppressUserConversions=*/false, /*AllowExplicit=*/true, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); } /// PerformContextuallyConvertToBool - Perform a contextual conversion @@ -3686,7 +3862,8 @@ TryContextuallyConvertToObjCId(Sema &S, Expr *From) { /*SuppressUserConversions=*/false, /*AllowExplicit=*/true, /*InOverloadResolution=*/false, - /*CStyle=*/false); + /*CStyle=*/false, + /*AllowObjCWritebackConversion=*/false); } /// PerformContextuallyConvertToObjCId - Perform a contextual conversion @@ -3980,7 +4157,9 @@ Sema::AddOverloadCandidate(FunctionDecl *Function, Candidate.Conversions[ArgIdx] = TryCopyInitialization(*this, Args[ArgIdx], ParamType, SuppressUserConversions, - /*InOverloadResolution=*/true); + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4153,7 +4332,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *Method, DeclAccessPair FoundDecl, Candidate.Conversions[ArgIdx + 1] = TryCopyInitialization(*this, Args[ArgIdx], ParamType, SuppressUserConversions, - /*InOverloadResolution=*/true); + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); if (Candidate.Conversions[ArgIdx + 1].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4356,25 +4537,26 @@ Sema::AddConversionCandidate(CXXConversionDecl *Conversion, CK_FunctionToPointerDecay, &ConversionRef, VK_RValue); - QualType CallResultType - = Conversion->getConversionType().getNonLValueExprType(Context); - if (RequireCompleteType(From->getLocStart(), CallResultType, 0)) { + QualType ConversionType = Conversion->getConversionType(); + if (RequireCompleteType(From->getLocStart(), ConversionType, 0)) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_final_conversion; return; } - ExprValueKind VK = Expr::getValueKindForType(Conversion->getConversionType()); + ExprValueKind VK = Expr::getValueKindForType(ConversionType); // Note that it is safe to allocate CallExpr on the stack here because // there are 0 arguments (i.e., nothing is allocated using ASTContext's // allocator). + QualType CallResultType = ConversionType.getNonLValueExprType(Context); CallExpr Call(Context, &ConversionFn, 0, 0, CallResultType, VK, From->getLocStart()); ImplicitConversionSequence ICS = TryCopyInitialization(*this, &Call, ToType, /*SuppressUserConversions=*/true, - /*InOverloadResolution=*/false); + /*InOverloadResolution=*/false, + /*AllowObjCWritebackConversion=*/false); switch (ICS.getKind()) { case ImplicitConversionSequence::StandardConversion: @@ -4544,7 +4726,9 @@ void Sema::AddSurrogateCandidate(CXXConversionDecl *Conversion, Candidate.Conversions[ArgIdx + 1] = TryCopyInitialization(*this, Args[ArgIdx], ParamType, /*SuppressUserConversions=*/false, - /*InOverloadResolution=*/false); + /*InOverloadResolution=*/false, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); if (Candidate.Conversions[ArgIdx + 1].isBad()) { Candidate.Viable = false; Candidate.FailureKind = ovl_fail_bad_conversion; @@ -4662,7 +4846,9 @@ void Sema::AddBuiltinCandidate(QualType ResultTy, QualType *ParamTys, Candidate.Conversions[ArgIdx] = TryCopyInitialization(*this, Args[ArgIdx], ParamTys[ArgIdx], ArgIdx == 0 && IsAssignmentOperator, - /*InOverloadResolution=*/false); + /*InOverloadResolution=*/false, + /*AllowObjCWritebackConversion=*/ + getLangOptions().ObjCAutoRefCount); } if (Candidate.Conversions[ArgIdx].isBad()) { Candidate.Viable = false; @@ -6417,7 +6603,8 @@ OverloadCandidateSet::BestViableFunction(Sema &S, SourceLocation Loc, // Best is the best viable function. if (Best->Function && - (Best->Function->isDeleted() || Best->Function->isUnavailable())) + (Best->Function->isDeleted() || + S.isFunctionConsideredUnavailable(Best->Function))) return OR_Deleted; return OR_Success; @@ -6619,6 +6806,17 @@ void DiagnoseBadConversion(Sema &S, OverloadCandidate *Cand, unsigned I) { return; } + if (FromQs.getObjCLifetime() != ToQs.getObjCLifetime()) { + S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_ownership) + << (unsigned) FnKind << FnDesc + << (FromExpr ? FromExpr->getSourceRange() : SourceRange()) + << FromTy + << FromQs.getObjCLifetime() << ToQs.getObjCLifetime() + << (unsigned) isObjectArgument << I+1; + MaybeEmitInheritedConstructorNote(S, Fn); + return; + } + if (FromQs.getObjCGCAttr() != ToQs.getObjCGCAttr()) { S.Diag(Fn->getLocation(), diag::note_ovl_candidate_bad_gc) << (unsigned) FnKind << FnDesc @@ -6901,7 +7099,8 @@ void NoteFunctionCandidate(Sema &S, OverloadCandidate *Cand, FunctionDecl *Fn = Cand->Function; // Note deleted candidates, but only if they're viable. - if (Cand->Viable && (Fn->isDeleted() || Fn->isUnavailable())) { + if (Cand->Viable && (Fn->isDeleted() || + S.isFunctionConsideredUnavailable(Fn))) { std::string FnDesc; OverloadCandidateKind FnKind = ClassifyOverloadCandidate(S, Fn, FnDesc); @@ -7152,7 +7351,9 @@ void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, = TryCopyInitialization(S, Args[ConvIdx], Cand->BuiltinTypes.ParamTypes[ConvIdx], SuppressUserConversions, - /*InOverloadResolution*/ true); + /*InOverloadResolution*/ true, + /*AllowObjCWritebackConversion=*/ + S.getLangOptions().ObjCAutoRefCount); return; } @@ -7163,7 +7364,9 @@ void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand, Cand->Conversions[ConvIdx] = TryCopyInitialization(S, Args[ArgIdx], Proto->getArgType(ArgIdx), SuppressUserConversions, - /*InOverloadResolution=*/true); + /*InOverloadResolution=*/true, + /*AllowObjCWritebackConversion=*/ + S.getLangOptions().ObjCAutoRefCount); else Cand->Conversions[ConvIdx].setEllipsis(); } @@ -7407,8 +7610,8 @@ private: QualType ResultTy; if (Context.hasSameUnqualifiedType(TargetFunctionType, FunDecl->getType()) || - IsNoReturnConversion(Context, FunDecl->getType(), TargetFunctionType, - ResultTy)) { + S.IsNoReturnConversion(FunDecl->getType(), TargetFunctionType, + ResultTy)) { Matches.push_back(std::make_pair(CurAccessFunPair, cast<FunctionDecl>(FunDecl->getCanonicalDecl()))); FoundNonTemplateFunction = true; @@ -7742,13 +7945,17 @@ static void AddOverloadedCallCandidate(Sema &S, TemplateArgumentListInfo *ExplicitTemplateArgs, Expr **Args, unsigned NumArgs, OverloadCandidateSet &CandidateSet, - bool PartialOverloading) { + bool PartialOverloading, + bool KnownValid) { NamedDecl *Callee = FoundDecl.getDecl(); if (isa<UsingShadowDecl>(Callee)) Callee = cast<UsingShadowDecl>(Callee)->getTargetDecl(); if (FunctionDecl *Func = dyn_cast<FunctionDecl>(Callee)) { - assert(!ExplicitTemplateArgs && "Explicit template arguments?"); + if (ExplicitTemplateArgs) { + assert(!KnownValid && "Explicit template arguments?"); + return; + } S.AddOverloadCandidate(Func, FoundDecl, Args, NumArgs, CandidateSet, false, PartialOverloading); return; @@ -7762,9 +7969,7 @@ static void AddOverloadedCallCandidate(Sema &S, return; } - assert(false && "unhandled case in overloaded call candidate"); - - // do nothing? + assert(!KnownValid && "unhandled case in overloaded call candidate"); } /// \brief Add the overload candidates named by callee and/or found by argument @@ -7815,7 +8020,7 @@ void Sema::AddOverloadedCallCandidates(UnresolvedLookupExpr *ULE, E = ULE->decls_end(); I != E; ++I) AddOverloadedCallCandidate(*this, I.getPair(), ExplicitTemplateArgs, Args, NumArgs, CandidateSet, - PartialOverloading); + PartialOverloading, /*KnownValid*/ true); if (ULE->requiresADL()) AddArgumentDependentLookupCandidates(ULE->getName(), /*Operator*/ false, @@ -7857,13 +8062,15 @@ DiagnoseTwoPhaseLookup(Sema &SemaRef, SourceLocation FnLoc, for (LookupResult::iterator I = R.begin(), E = R.end(); I != E; ++I) AddOverloadedCallCandidate(SemaRef, I.getPair(), ExplicitTemplateArgs, Args, NumArgs, - Candidates, false); + Candidates, false, /*KnownValid*/ false); OverloadCandidateSet::iterator Best; - if (Candidates.BestViableFunction(SemaRef, FnLoc, Best) != OR_Success) + if (Candidates.BestViableFunction(SemaRef, FnLoc, Best) != OR_Success) { // No viable functions. Don't bother the user with notes for functions // which don't work and shouldn't be found anyway. + R.clear(); return false; + } // Find the namespaces where ADL would have looked, and suggest // declaring the function there instead. @@ -8673,7 +8880,10 @@ Sema::CreateOverloadedArraySubscriptExpr(SourceLocation LLoc, ResultTy = ResultTy.getNonLValueExprType(Context); // Build the actual expression node. - ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, LLoc); + DeclarationNameLoc LocInfo; + LocInfo.CXXOperatorName.BeginOpNameLoc = LLoc.getRawEncoding(); + LocInfo.CXXOperatorName.EndOpNameLoc = RLoc.getRawEncoding(); + ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl, LLoc, LocInfo); if (FnExpr.isInvalid()) return ExprError(); @@ -8963,13 +9173,14 @@ Sema::BuildCallToMemberFunction(Scope *S, Expr *MemExprE, TheCall->getMethodDecl()->isPure()) { const CXXMethodDecl *MD = TheCall->getMethodDecl(); - if (isa<CXXThisExpr>(MemExpr->getBase()->IgnoreParenCasts())) + if (isa<CXXThisExpr>(MemExpr->getBase()->IgnoreParenCasts())) { Diag(MemExpr->getLocStart(), diag::warn_call_to_pure_virtual_member_function_from_ctor_dtor) << MD->getDeclName() << isa<CXXDestructorDecl>(CurContext) << MD->getParent()->getDeclName(); Diag(MD->getLocStart(), diag::note_previous_decl) << MD->getDeclName(); + } } return MaybeBindToTemporary(TheCall); } diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index d7c0a543eede..65f431d46075 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -66,8 +66,30 @@ void Sema::ActOnForEachDeclStmt(DeclGroupPtrTy dg) { // If we have an invalid decl, just return. if (DG.isNull() || !DG.isSingleDecl()) return; + VarDecl *var = cast<VarDecl>(DG.getSingleDecl()); + // suppress any potential 'unused variable' warning. - DG.getSingleDecl()->setUsed(); + var->setUsed(); + + // foreach variables are never actually initialized in the way that + // the parser came up with. + var->setInit(0); + + // In ARC, we don't need to retain the iteration variable of a fast + // enumeration loop. Rather than actually trying to catch that + // during declaration processing, we remove the consequences here. + if (getLangOptions().ObjCAutoRefCount) { + QualType type = var->getType(); + + // Only do this if we inferred the lifetime. Inferred lifetime + // will show up as a local qualifier because explicit lifetime + // should have shown up as an AttributedType instead. + if (type.getLocalQualifiers().getObjCLifetime() == Qualifiers::OCL_Strong) { + // Add 'const' and mark the variable as pseudo-strong. + var->setType(type.withConst()); + var->setARCPseudoStrong(true); + } + } } void Sema::DiagnoseUnusedExprResult(const Stmt *S) { @@ -114,6 +136,10 @@ void Sema::DiagnoseUnusedExprResult(const Stmt *S) { } } } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(E)) { + if (getLangOptions().ObjCAutoRefCount && ME->isDelegateInitCall()) { + Diag(Loc, diag::err_arc_unused_init_message) << R1; + return; + } const ObjCMethodDecl *MD = ME->getMethodDecl(); if (MD && MD->getAttr<WarnUnusedResultAttr>()) { Diag(Loc, diag::warn_unused_call) << R1 << R2 << "warn_unused_result"; @@ -951,14 +977,13 @@ Sema::ActOnObjCForCollectionStmt(SourceLocation ForLoc, return StmtError(Diag((*DS->decl_begin())->getLocation(), diag::err_toomany_element_decls)); - Decl *D = DS->getSingleDecl(); - FirstType = cast<ValueDecl>(D)->getType(); + VarDecl *D = cast<VarDecl>(DS->getSingleDecl()); + FirstType = D->getType(); // C99 6.8.5p3: The declaration part of a 'for' statement shall only // declare identifiers for objects having storage class 'auto' or // 'register'. - VarDecl *VD = cast<VarDecl>(D); - if (VD->isLocalVarDecl() && !VD->hasLocalStorage()) - return StmtError(Diag(VD->getLocation(), + if (!D->hasLocalStorage()) + return StmtError(Diag(D->getLocation(), diag::err_non_variable_decl_in_for)); } else { Expr *FirstE = cast<Expr>(First); @@ -1047,6 +1072,13 @@ static bool FinishForRangeVarDecl(Sema &SemaRef, VarDecl *Decl, Expr *Init, Decl->setTypeSourceInfo(InitTSI); Decl->setType(InitTSI->getType()); + // In ARC, infer lifetime. + // FIXME: ARC may want to turn this into 'const __unsafe_unretained' if + // we're doing the equivalent of fast iteration. + if (SemaRef.getLangOptions().ObjCAutoRefCount && + SemaRef.inferObjCARCLifetime(Decl)) + Decl->setInvalidDecl(); + SemaRef.AddInitializerToDecl(Decl, Init, /*DirectInit=*/false, /*TypeMayContainAuto=*/false); SemaRef.FinalizeDeclaration(Decl); @@ -1374,6 +1406,9 @@ Sema::BuildCXXForRangeStmt(SourceLocation ForLoc, SourceLocation ColonLoc, if (LoopVar->isInvalidDecl()) NoteForRangeBeginEndFunction(*this, BeginExpr.get(), BEF_begin); } + } else { + // The range is implicitly used as a placeholder when it is dependent. + RangeVar->setUsed(); } return Owned(new (Context) CXXForRangeStmt(RangeDS, @@ -1508,7 +1543,8 @@ ExprResult Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, const VarDecl *NRVOCandidate, QualType ResultType, - Expr *Value) { + Expr *Value, + bool AllowNRVO) { // C++0x [class.copy]p33: // When the criteria for elision of a copy operation are met or would // be met save for the fact that the source object is a function @@ -1516,7 +1552,8 @@ Sema::PerformMoveOrCopyInitialization(const InitializedEntity &Entity, // overload resolution to select the constructor for the copy is first // performed as if the object were designated by an rvalue. ExprResult Res = ExprError(); - if (NRVOCandidate || getCopyElisionCandidate(ResultType, Value, true)) { + if (AllowNRVO && + (NRVOCandidate || getCopyElisionCandidate(ResultType, Value, true))) { ImplicitCastExpr AsRvalue(ImplicitCastExpr::OnStack, Value->getType(), CK_LValueToRValue, Value, VK_XValue); @@ -1726,8 +1763,17 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (D != diag::ext_return_has_void_expr || !getLangOptions().CPlusPlus) { NamedDecl *CurDecl = getCurFunctionOrMethodDecl(); + + int FunctionKind = 0; + if (isa<ObjCMethodDecl>(CurDecl)) + FunctionKind = 1; + else if (isa<CXXConstructorDecl>(CurDecl)) + FunctionKind = 2; + else if (isa<CXXDestructorDecl>(CurDecl)) + FunctionKind = 3; + Diag(ReturnLoc, D) - << CurDecl->getDeclName() << isa<ObjCMethodDecl>(CurDecl) + << CurDecl->getDeclName() << FunctionKind << RetValExp->getSourceRange(); } } @@ -1756,7 +1802,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { // overlap restriction of subclause 6.5.16.1 does not apply to the case of // function return. - // In C++ the return statement is handled via a copy initialization. + // In C++ the return statement is handled via a copy initialization, // the C version of which boils down to CheckSingleAssignmentConstraints. NRVOCandidate = getCopyElisionCandidate(FnRetType, RetValExp, false); InitializedEntity Entity = InitializedEntity::InitializeResult(ReturnLoc, @@ -1797,7 +1843,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) { if (getLangOptions().CPlusPlus && FnRetType->isRecordType() && !CurContext->isDependentContext()) FunctionScopes.back()->Returns.push_back(Result); - + return Owned(Result); } @@ -1955,7 +2001,7 @@ StmtResult Sema::ActOnAsmStmt(SourceLocation AsmLoc, bool IsSimple, llvm::StringRef Clobber = Literal->getString(); - if (!Context.Target.isValidGCCRegisterName(Clobber)) + if (!Context.Target.isValidClobber(Clobber)) return StmtError(Diag(Literal->getLocStart(), diag::err_asm_unknown_register_name) << Clobber); } @@ -2179,6 +2225,12 @@ Sema::ActOnCXXCatchBlock(SourceLocation CatchLoc, Decl *ExDecl, HandlerBlock)); } +StmtResult +Sema::ActOnObjCAutoreleasePoolStmt(SourceLocation AtLoc, Stmt *Body) { + getCurFunction()->setHasBranchProtectedScope(); + return Owned(new (Context) ObjCAutoreleasePoolStmt(AtLoc, Body)); +} + namespace { class TypeWithHandler { @@ -2309,4 +2361,3 @@ Sema::ActOnSEHFinallyBlock(SourceLocation Loc, assert(Block); return Owned(SEHFinallyStmt::Create(Context,Loc,Block)); } - diff --git a/lib/Sema/SemaTemplate.cpp b/lib/Sema/SemaTemplate.cpp index 5d4caacd8124..3ac190e1d4c8 100644 --- a/lib/Sema/SemaTemplate.cpp +++ b/lib/Sema/SemaTemplate.cpp @@ -294,26 +294,31 @@ void Sema::LookupTemplateName(LookupResult &Found, if (Found.empty() && !isDependent) { // If we did not find any names, attempt to correct any typos. DeclarationName Name = Found.getLookupName(); - if (DeclarationName Corrected = CorrectTypo(Found, S, &SS, LookupCtx, - false, CTC_CXXCasts)) { + Found.clear(); + if (TypoCorrection Corrected = CorrectTypo(Found.getLookupNameInfo(), + Found.getLookupKind(), S, &SS, + LookupCtx, false, + CTC_CXXCasts)) { + Found.setLookupName(Corrected.getCorrection()); + if (Corrected.getCorrectionDecl()) + Found.addDecl(Corrected.getCorrectionDecl()); FilterAcceptableTemplateNames(Found); if (!Found.empty()) { + std::string CorrectedStr(Corrected.getAsString(getLangOptions())); + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions())); if (LookupCtx) Diag(Found.getNameLoc(), diag::err_no_member_template_suggest) - << Name << LookupCtx << Found.getLookupName() << SS.getRange() - << FixItHint::CreateReplacement(Found.getNameLoc(), - Found.getLookupName().getAsString()); + << Name << LookupCtx << CorrectedQuotedStr << SS.getRange() + << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr); else Diag(Found.getNameLoc(), diag::err_no_template_suggest) - << Name << Found.getLookupName() - << FixItHint::CreateReplacement(Found.getNameLoc(), - Found.getLookupName().getAsString()); + << Name << CorrectedQuotedStr + << FixItHint::CreateReplacement(Found.getNameLoc(), CorrectedStr); if (TemplateDecl *Template = Found.getAsSingle<TemplateDecl>()) Diag(Template->getLocation(), diag::note_previous_decl) - << Template->getDeclName(); + << CorrectedQuotedStr; } } else { - Found.clear(); Found.setLookupName(Name); } } @@ -1856,7 +1861,8 @@ void Sema::NoteAllFoundTemplates(TemplateName Name) { QualType Sema::CheckTemplateIdType(TemplateName Name, SourceLocation TemplateLoc, TemplateArgumentListInfo &TemplateArgs) { - DependentTemplateName *DTN = Name.getAsDependentTemplateName(); + DependentTemplateName *DTN + = Name.getUnderlying().getAsDependentTemplateName(); if (DTN && DTN->isIdentifier()) // When building a template-id where the template-name is dependent, // assume the template is a type template. Either our assumption is @@ -1892,6 +1898,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, QualType CanonType; + bool InstantiationDependent = false; if (TypeAliasTemplateDecl *AliasTemplate = dyn_cast<TypeAliasTemplateDecl>(Template)) { // Find the canonical type for this type alias template specialization. @@ -1917,7 +1924,7 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, return QualType(); } else if (Name.isDependent() || TemplateSpecializationType::anyDependentTemplateArguments( - TemplateArgs)) { + TemplateArgs, InstantiationDependent)) { // This class template specialization is a dependent // type. Therefore, its canonical type is another class template // specialization type that contains all of the converted @@ -2357,8 +2364,20 @@ bool Sema::CheckTemplateTypeArgument(TemplateTypeParmDecl *Param, return true; // Add the converted template type argument. - Converted.push_back( - TemplateArgument(Context.getCanonicalType(Arg.getAsType()))); + QualType ArgType = Context.getCanonicalType(Arg.getAsType()); + + // Objective-C ARC: + // If an explicitly-specified template argument type is a lifetime type + // with no lifetime qualifier, the __strong lifetime qualifier is inferred. + if (getLangOptions().ObjCAutoRefCount && + ArgType->isObjCLifetimeType() && + !ArgType.getObjCLifetime()) { + Qualifiers Qs; + Qs.setObjCLifetime(Qualifiers::OCL_Strong); + ArgType = Context.getQualifiedType(ArgType, Qs); + } + + Converted.push_back(TemplateArgument(ArgType)); return false; } @@ -2912,16 +2931,6 @@ bool Sema::CheckTemplateArgumentList(TemplateDecl *Template, // arguments, just break out now and we'll fill in the argument pack below. if ((*Param)->isTemplateParameterPack()) break; - - // If our template is a template template parameter that hasn't acquired - // its proper context yet (e.g., because we're using the template template - // parameter in the signature of a function template, before we've built - // the function template itself), don't attempt substitution of default - // template arguments at this point: we don't have enough context to - // do it properly. - if (isTemplateTemplateParameter && - Template->getDeclContext()->isTranslationUnit()) - break; // We have a default template argument that we will use. TemplateArgumentLoc Arg; @@ -3307,8 +3316,7 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, QualType ArgType = Arg->getType(); // See through any implicit casts we added to fix the type. - while (ImplicitCastExpr *Cast = dyn_cast<ImplicitCastExpr>(Arg)) - Arg = Cast->getSubExpr(); + Arg = Arg->IgnoreImpCasts(); // C++ [temp.arg.nontype]p1: // @@ -3321,7 +3329,6 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, // expressed as & id-expression where the & is optional if // the name refers to a function or array, or if the // corresponding template-parameter is a reference; or - DeclRefExpr *DRE = 0; // In C++98/03 mode, give an extension warning on any extra parentheses. // See http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#773 @@ -3337,29 +3344,30 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, Arg = Parens->getSubExpr(); } + while (SubstNonTypeTemplateParmExpr *subst = + dyn_cast<SubstNonTypeTemplateParmExpr>(Arg)) + Arg = subst->getReplacement()->IgnoreImpCasts(); + bool AddressTaken = false; SourceLocation AddrOpLoc; if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg)) { if (UnOp->getOpcode() == UO_AddrOf) { - // Support &__uuidof(class_with_uuid) as a non-type template argument. - // Very common in Microsoft COM headers. - if (S.getLangOptions().Microsoft && - isa<CXXUuidofExpr>(UnOp->getSubExpr())) { - Converted = TemplateArgument(ArgIn); - return false; - } - - DRE = dyn_cast<DeclRefExpr>(UnOp->getSubExpr()); + Arg = UnOp->getSubExpr(); AddressTaken = true; AddrOpLoc = UnOp->getOperatorLoc(); } - } else { - if (S.getLangOptions().Microsoft && isa<CXXUuidofExpr>(Arg)) { - Converted = TemplateArgument(ArgIn); - return false; - } - DRE = dyn_cast<DeclRefExpr>(Arg); } + + if (S.getLangOptions().Microsoft && isa<CXXUuidofExpr>(Arg)) { + Converted = TemplateArgument(ArgIn); + return false; + } + + while (SubstNonTypeTemplateParmExpr *subst = + dyn_cast<SubstNonTypeTemplateParmExpr>(Arg)) + Arg = subst->getReplacement()->IgnoreImpCasts(); + + DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Arg); if (!DRE) { S.Diag(Arg->getLocStart(), diag::err_template_arg_not_decl_ref) << Arg->getSourceRange(); @@ -3513,9 +3521,11 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, return true; } + bool ObjCLifetimeConversion; if (ParamType->isPointerType() && !ParamType->getAs<PointerType>()->getPointeeType()->isFunctionType() && - S.IsQualificationConversion(ArgType, ParamType, false)) { + S.IsQualificationConversion(ArgType, ParamType, false, + ObjCLifetimeConversion)) { // For pointer-to-object types, qualification conversions are // permitted. } else { @@ -3552,10 +3562,10 @@ CheckTemplateArgumentAddressOfObjectOrFunction(Sema &S, // We can't perform this conversion or binding. if (ParamType->isReferenceType()) S.Diag(Arg->getLocStart(), diag::err_template_arg_no_ref_bind) - << ParamType << Arg->getType() << Arg->getSourceRange(); + << ParamType << ArgIn->getType() << Arg->getSourceRange(); else S.Diag(Arg->getLocStart(), diag::err_template_arg_not_convertible) - << Arg->getType() << ParamType << Arg->getSourceRange(); + << ArgIn->getType() << ParamType << Arg->getSourceRange(); S.Diag(Param->getLocation(), diag::note_template_param_here); return true; } @@ -3599,6 +3609,10 @@ bool Sema::CheckTemplateArgumentPointerToMember(Expr *Arg, Arg = Parens->getSubExpr(); } + while (SubstNonTypeTemplateParmExpr *subst = + dyn_cast<SubstNonTypeTemplateParmExpr>(Arg)) + Arg = subst->getReplacement()->IgnoreImpCasts(); + // A pointer-to-member constant written &Class::member. if (UnaryOperator *UnOp = dyn_cast<UnaryOperator>(Arg)) { if (UnOp->getOpcode() == UO_AddrOf) { @@ -3875,8 +3889,9 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, return Owned(Arg); } + bool ObjCLifetimeConversion; if (IsQualificationConversion(ArgType, ParamType.getNonReferenceType(), - false)) { + false, ObjCLifetimeConversion)) { Arg = ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg)).take(); } else if (!Context.hasSameUnqualifiedType(ArgType, ParamType.getNonReferenceType())) { @@ -3943,9 +3958,11 @@ ExprResult Sema::CheckTemplateArgument(NonTypeTemplateParmDecl *Param, // member, qualification conversions (4.4) are applied. assert(ParamType->isMemberPointerType() && "Only pointers to members remain"); + bool ObjCLifetimeConversion; if (Context.hasSameUnqualifiedType(ParamType, ArgType)) { // Types match exactly: nothing more to do here. - } else if (IsQualificationConversion(ArgType, ParamType, false)) { + } else if (IsQualificationConversion(ArgType, ParamType, false, + ObjCLifetimeConversion)) { Arg = ImpCastExprToType(Arg, ParamType, CK_NoOp, CastCategory(Arg)).take(); } else { // We can't perform this conversion. @@ -4053,8 +4070,10 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, // We might need to perform a trailing qualification conversion, since // the element type on the parameter could be more qualified than the // element type in the expression we constructed. + bool ObjCLifetimeConversion; if (IsQualificationConversion(((Expr*) RefExpr.get())->getType(), - ParamType.getUnqualifiedType(), false)) + ParamType.getUnqualifiedType(), false, + ObjCLifetimeConversion)) RefExpr = ImpCastExprToType(RefExpr.take(), ParamType.getUnqualifiedType(), CK_NoOp); assert(!RefExpr.isInvalid() && @@ -4818,10 +4837,12 @@ Sema::ActOnClassTemplateSpecialization(Scope *S, unsigned TagSpec, Converted)) return true; + bool InstantiationDependent; if (!Name.isDependent() && !TemplateSpecializationType::anyDependentTemplateArguments( TemplateArgs.getArgumentArray(), - TemplateArgs.size())) { + TemplateArgs.size(), + InstantiationDependent)) { Diag(TemplateNameLoc, diag::err_partial_spec_fully_specialized) << ClassTemplate->getDeclName(); isPartialSpecialization = false; @@ -5346,7 +5367,7 @@ Sema::CheckDependentFunctionTemplateSpecialization(FunctionDecl *FD, /// explicitly provided as in, e.g., \c void sort<>(char*, char*); /// as it anyway contains info on the angle brackets locations. /// -/// \param PrevDecl the set of declarations that may be specialized by +/// \param Previous the set of declarations that may be specialized by /// this function specialization. bool Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, @@ -5410,13 +5431,12 @@ Sema::CheckFunctionTemplateSpecialization(FunctionDecl *FD, FunctionTemplateSpecializationInfo *SpecInfo = Specialization->getTemplateSpecializationInfo(); assert(SpecInfo && "Function template specialization info missing?"); - { - // Note: do not overwrite location info if previous template - // specialization kind was explicit. - TemplateSpecializationKind TSK = SpecInfo->getTemplateSpecializationKind(); - if (TSK == TSK_Undeclared || TSK == TSK_ImplicitInstantiation) - Specialization->setLocation(FD->getLocation()); - } + + // Note: do not overwrite location info if previous template + // specialization kind was explicit. + TemplateSpecializationKind TSK = SpecInfo->getTemplateSpecializationKind(); + if (TSK == TSK_Undeclared || TSK == TSK_ImplicitInstantiation) + Specialization->setLocation(FD->getLocation()); // FIXME: Check if the prior specialization has a point of instantiation. // If so, we have run afoul of . diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index 7d0ce8b2e71d..dcb4ff286060 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -12,7 +12,6 @@ #include "clang/Sema/Sema.h" #include "clang/Sema/DeclSpec.h" -#include "clang/Sema/SemaDiagnostic.h" // FIXME: temporary! #include "clang/Sema/Template.h" #include "clang/Sema/TemplateDeduction.h" #include "clang/AST/ASTContext.h" @@ -820,6 +819,11 @@ static bool hasInconsistentOrSupersetQualifiersOf(QualType ParamType, ParamQs.hasAddressSpace()) return true; + // Mismatched (but not missing) Objective-C lifetime qualifiers. + if (ParamQs.getObjCLifetime() != ArgQs.getObjCLifetime() && + ParamQs.hasObjCLifetime()) + return true; + // CVR qualifier superset. return (ParamQs.getCVRQualifiers() != ArgQs.getCVRQualifiers()) && ((ParamQs.getCVRQualifiers() | ArgQs.getCVRQualifiers()) @@ -1010,6 +1014,17 @@ DeduceTemplateArguments(Sema &S, DeducedQs.removeObjCGCAttr(); if (ParamQs.hasAddressSpace()) DeducedQs.removeAddressSpace(); + if (ParamQs.hasObjCLifetime()) + DeducedQs.removeObjCLifetime(); + + // Objective-C ARC: + // If template deduction would produce an argument type with lifetime type + // but no lifetime qualifier, the __strong lifetime qualifier is inferred. + if (S.getLangOptions().ObjCAutoRefCount && + DeducedType->isObjCLifetimeType() && + !DeducedQs.hasObjCLifetime()) + DeducedQs.setObjCLifetime(Qualifiers::OCL_Strong); + DeducedType = S.Context.getQualifiedType(DeducedType.getUnqualifiedType(), DeducedQs); @@ -1054,10 +1069,39 @@ DeduceTemplateArguments(Sema &S, } switch (Param->getTypeClass()) { - // No deduction possible for these types + // Non-canonical types cannot appear here. +#define NON_CANONICAL_TYPE(Class, Base) \ + case Type::Class: llvm_unreachable("deducing non-canonical type: " #Class); +#define TYPE(Class, Base) +#include "clang/AST/TypeNodes.def" + + case Type::TemplateTypeParm: + case Type::SubstTemplateTypeParmPack: + llvm_unreachable("Type nodes handled above"); + + // These types cannot be used in templates or cannot be dependent, so + // deduction always fails. case Type::Builtin: + case Type::VariableArray: + case Type::Vector: + case Type::FunctionNoProto: + case Type::Record: + case Type::Enum: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::ObjCObjectPointer: return Sema::TDK_NonDeducedMismatch; + // _Complex T [placeholder extension] + case Type::Complex: + if (const ComplexType *ComplexArg = Arg->getAs<ComplexType>()) + return DeduceTemplateArguments(S, TemplateParams, + cast<ComplexType>(Param)->getElementType(), + ComplexArg->getElementType(), + Info, Deduced, TDF); + + return Sema::TDK_NonDeducedMismatch; + // T * case Type::Pointer: { QualType PointeeType; @@ -1361,17 +1405,107 @@ DeduceTemplateArguments(Sema &S, Deduced, 0); } + // (clang extension) + // + // T __attribute__(((ext_vector_type(<integral constant>)))) + case Type::ExtVector: { + const ExtVectorType *VectorParam = cast<ExtVectorType>(Param); + if (const ExtVectorType *VectorArg = dyn_cast<ExtVectorType>(Arg)) { + // Make sure that the vectors have the same number of elements. + if (VectorParam->getNumElements() != VectorArg->getNumElements()) + return Sema::TDK_NonDeducedMismatch; + + // Perform deduction on the element types. + return DeduceTemplateArguments(S, TemplateParams, + VectorParam->getElementType(), + VectorArg->getElementType(), + Info, Deduced, + TDF); + } + + if (const DependentSizedExtVectorType *VectorArg + = dyn_cast<DependentSizedExtVectorType>(Arg)) { + // We can't check the number of elements, since the argument has a + // dependent number of elements. This can only occur during partial + // ordering. + + // Perform deduction on the element types. + return DeduceTemplateArguments(S, TemplateParams, + VectorParam->getElementType(), + VectorArg->getElementType(), + Info, Deduced, + TDF); + } + + return Sema::TDK_NonDeducedMismatch; + } + + // (clang extension) + // + // T __attribute__(((ext_vector_type(N)))) + case Type::DependentSizedExtVector: { + const DependentSizedExtVectorType *VectorParam + = cast<DependentSizedExtVectorType>(Param); + + if (const ExtVectorType *VectorArg = dyn_cast<ExtVectorType>(Arg)) { + // Perform deduction on the element types. + if (Sema::TemplateDeductionResult Result + = DeduceTemplateArguments(S, TemplateParams, + VectorParam->getElementType(), + VectorArg->getElementType(), + Info, Deduced, + TDF)) + return Result; + + // Perform deduction on the vector size, if we can. + NonTypeTemplateParmDecl *NTTP + = getDeducedParameterFromExpr(VectorParam->getSizeExpr()); + if (!NTTP) + return Sema::TDK_Success; + + llvm::APSInt ArgSize(S.Context.getTypeSize(S.Context.IntTy), false); + ArgSize = VectorArg->getNumElements(); + return DeduceNonTypeTemplateArgument(S, NTTP, ArgSize, S.Context.IntTy, + false, Info, Deduced); + } + + if (const DependentSizedExtVectorType *VectorArg + = dyn_cast<DependentSizedExtVectorType>(Arg)) { + // Perform deduction on the element types. + if (Sema::TemplateDeductionResult Result + = DeduceTemplateArguments(S, TemplateParams, + VectorParam->getElementType(), + VectorArg->getElementType(), + Info, Deduced, + TDF)) + return Result; + + // Perform deduction on the vector size, if we can. + NonTypeTemplateParmDecl *NTTP + = getDeducedParameterFromExpr(VectorParam->getSizeExpr()); + if (!NTTP) + return Sema::TDK_Success; + + return DeduceNonTypeTemplateArgument(S, NTTP, VectorArg->getSizeExpr(), + Info, Deduced); + } + + return Sema::TDK_NonDeducedMismatch; + } + case Type::TypeOfExpr: case Type::TypeOf: case Type::DependentName: + case Type::UnresolvedUsing: + case Type::Decltype: + case Type::UnaryTransform: + case Type::Auto: + case Type::DependentTemplateSpecialization: + case Type::PackExpansion: // No template argument deduction for these types return Sema::TDK_Success; - - default: - break; } - // FIXME: Many more cases to go (to go). return Sema::TDK_Success; } @@ -2189,15 +2323,108 @@ Sema::SubstituteExplicitTemplateArguments( return TDK_Success; } +/// \brief Check whether the deduced argument type for a call to a function +/// template matches the actual argument type per C++ [temp.deduct.call]p4. +static bool +CheckOriginalCallArgDeduction(Sema &S, Sema::OriginalCallArg OriginalArg, + QualType DeducedA) { + ASTContext &Context = S.Context; + + QualType A = OriginalArg.OriginalArgType; + QualType OriginalParamType = OriginalArg.OriginalParamType; + + // Check for type equality (top-level cv-qualifiers are ignored). + if (Context.hasSameUnqualifiedType(A, DeducedA)) + return false; + + // Strip off references on the argument types; they aren't needed for + // the following checks. + if (const ReferenceType *DeducedARef = DeducedA->getAs<ReferenceType>()) + DeducedA = DeducedARef->getPointeeType(); + if (const ReferenceType *ARef = A->getAs<ReferenceType>()) + A = ARef->getPointeeType(); + + // C++ [temp.deduct.call]p4: + // [...] However, there are three cases that allow a difference: + // - If the original P is a reference type, the deduced A (i.e., the + // type referred to by the reference) can be more cv-qualified than + // the transformed A. + if (const ReferenceType *OriginalParamRef + = OriginalParamType->getAs<ReferenceType>()) { + // We don't want to keep the reference around any more. + OriginalParamType = OriginalParamRef->getPointeeType(); + + Qualifiers AQuals = A.getQualifiers(); + Qualifiers DeducedAQuals = DeducedA.getQualifiers(); + if (AQuals == DeducedAQuals) { + // Qualifiers match; there's nothing to do. + } else if (!DeducedAQuals.compatiblyIncludes(AQuals)) { + return true; + } else { + // Qualifiers are compatible, so have the argument type adopt the + // deduced argument type's qualifiers as if we had performed the + // qualification conversion. + A = Context.getQualifiedType(A.getUnqualifiedType(), DeducedAQuals); + } + } + + // - The transformed A can be another pointer or pointer to member + // type that can be converted to the deduced A via a qualification + // conversion. + // + // Also allow conversions which merely strip [[noreturn]] from function types + // (recursively) as an extension. + // FIXME: Currently, this doesn't place nicely with qualfication conversions. + bool ObjCLifetimeConversion = false; + QualType ResultTy; + if ((A->isAnyPointerType() || A->isMemberPointerType()) && + (S.IsQualificationConversion(A, DeducedA, false, + ObjCLifetimeConversion) || + S.IsNoReturnConversion(A, DeducedA, ResultTy))) + return false; + + + // - If P is a class and P has the form simple-template-id, then the + // transformed A can be a derived class of the deduced A. [...] + // [...] Likewise, if P is a pointer to a class of the form + // simple-template-id, the transformed A can be a pointer to a + // derived class pointed to by the deduced A. + if (const PointerType *OriginalParamPtr + = OriginalParamType->getAs<PointerType>()) { + if (const PointerType *DeducedAPtr = DeducedA->getAs<PointerType>()) { + if (const PointerType *APtr = A->getAs<PointerType>()) { + if (A->getPointeeType()->isRecordType()) { + OriginalParamType = OriginalParamPtr->getPointeeType(); + DeducedA = DeducedAPtr->getPointeeType(); + A = APtr->getPointeeType(); + } + } + } + } + + if (Context.hasSameUnqualifiedType(A, DeducedA)) + return false; + + if (A->isRecordType() && isSimpleTemplateIdType(OriginalParamType) && + S.IsDerivedFrom(A, DeducedA)) + return false; + + return true; +} + /// \brief Finish template argument deduction for a function template, /// checking the deduced template arguments for completeness and forming /// the function template specialization. +/// +/// \param OriginalCallArgs If non-NULL, the original call arguments against +/// which the deduced argument types should be compared. Sema::TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, llvm::SmallVectorImpl<DeducedTemplateArgument> &Deduced, unsigned NumExplicitlySpecified, FunctionDecl *&Specialization, - TemplateDeductionInfo &Info) { + TemplateDeductionInfo &Info, + llvm::SmallVectorImpl<OriginalCallArg> const *OriginalCallArgs) { TemplateParameterList *TemplateParams = FunctionTemplate->getTemplateParameters(); @@ -2351,6 +2578,24 @@ Sema::FinishTemplateArgumentDeduction(FunctionTemplateDecl *FunctionTemplate, !Trap.hasErrorOccurred()) Info.take(); + if (OriginalCallArgs) { + // C++ [temp.deduct.call]p4: + // In general, the deduction process attempts to find template argument + // values that will make the deduced A identical to A (after the type A + // is transformed as described above). [...] + for (unsigned I = 0, N = OriginalCallArgs->size(); I != N; ++I) { + OriginalCallArg OriginalArg = (*OriginalCallArgs)[I]; + unsigned ParamIdx = OriginalArg.ArgIdx; + + if (ParamIdx >= Specialization->getNumParams()) + continue; + + QualType DeducedA = Specialization->getParamDecl(ParamIdx)->getType(); + if (CheckOriginalCallArgDeduction(*this, OriginalArg, DeducedA)) + return Sema::TDK_SubstitutionFailure; + } + } + // There may have been an error that did not prevent us from constructing a // declaration. Mark the declaration invalid and return with a substitution // failure. @@ -2594,6 +2839,10 @@ static bool AdjustFunctionParmAndArgTypesForDeduction(Sema &S, return false; } +static bool hasDeducibleTemplateParameters(Sema &S, + FunctionTemplateDecl *FunctionTemplate, + QualType T); + /// \brief Perform template argument deduction from a function call /// (C++ [temp.deduct.call]). /// @@ -2675,10 +2924,12 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, // Deduce template arguments from the function parameters. Deduced.resize(TemplateParams->size()); unsigned ArgIdx = 0; + llvm::SmallVector<OriginalCallArg, 4> OriginalCallArgs; for (unsigned ParamIdx = 0, NumParams = ParamTypes.size(); ParamIdx != NumParams; ++ParamIdx) { - QualType ParamType = ParamTypes[ParamIdx]; - + QualType OrigParamType = ParamTypes[ParamIdx]; + QualType ParamType = OrigParamType; + const PackExpansionType *ParamExpansion = dyn_cast<PackExpansionType>(ParamType); if (!ParamExpansion) { @@ -2688,20 +2939,25 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, Expr *Arg = Args[ArgIdx++]; QualType ArgType = Arg->getType(); + unsigned TDF = 0; if (AdjustFunctionParmAndArgTypesForDeduction(*this, TemplateParams, ParamType, ArgType, Arg, TDF)) continue; + // Keep track of the argument type and corresponding parameter index, + // so we can check for compatibility between the deduced A and A. + if (hasDeducibleTemplateParameters(*this, FunctionTemplate, ParamType)) + OriginalCallArgs.push_back(OriginalCallArg(OrigParamType, ArgIdx-1, + ArgType)); + if (TemplateDeductionResult Result = ::DeduceTemplateArguments(*this, TemplateParams, ParamType, ArgType, Info, Deduced, TDF)) return Result; - // FIXME: we need to check that the deduced A is the same as A, - // modulo the various allowed differences. continue; } @@ -2747,9 +3003,11 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, for (; ArgIdx < NumArgs; ++ArgIdx) { HasAnyArguments = true; - ParamType = ParamPattern; + QualType OrigParamType = ParamPattern; + ParamType = OrigParamType; Expr *Arg = Args[ArgIdx]; QualType ArgType = Arg->getType(); + unsigned TDF = 0; if (AdjustFunctionParmAndArgTypesForDeduction(*this, TemplateParams, ParamType, ArgType, Arg, @@ -2760,6 +3018,12 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, break; } + // Keep track of the argument type and corresponding argument index, + // so we can check for compatibility between the deduced A and A. + if (hasDeducibleTemplateParameters(*this, FunctionTemplate, ParamType)) + OriginalCallArgs.push_back(OriginalCallArg(OrigParamType, ArgIdx, + ArgType)); + if (TemplateDeductionResult Result = ::DeduceTemplateArguments(*this, TemplateParams, ParamType, ArgType, Info, Deduced, @@ -2792,7 +3056,7 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, return FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, NumExplicitlySpecified, - Specialization, Info); + Specialization, Info, &OriginalCallArgs); } /// \brief Deduce template arguments when taking the address of a function @@ -2969,9 +3233,6 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate, P, A, Info, Deduced, TDF)) return Result; - // FIXME: we need to check that the deduced A is the same as A, - // modulo the various allowed differences. - // Finish template argument deduction. LocalInstantiationScope InstScope(*this); FunctionDecl *Spec = 0; @@ -3099,8 +3360,19 @@ Sema::DeduceAutoType(TypeSourceInfo *Type, Expr *Init, QualType DeducedType = Deduced[0].getAsType(); if (DeducedType.isNull()) return false; - + Result = SubstituteAutoTransform(*this, DeducedType).TransformType(Type); + + // Check that the deduced argument type is compatible with the original + // argument type per C++ [temp.deduct.call]p4. + if (Result && + CheckOriginalCallArgDeduction(*this, + Sema::OriginalCallArg(FuncParam,0,InitType), + Result->getType())) { + Result = 0; + return false; + } + return true; } @@ -3227,8 +3499,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S, case TPOC_Other: // - In other contexts (14.6.6.2) the function template's function type // is used. - // FIXME: Don't we actually want to perform the adjustments on the parameter - // types? if (DeduceTemplateArguments(S, TemplateParams, FD2->getType(), FD1->getType(), Info, Deduced, TDF_None, /*PartialOrdering=*/true, RefParamComparisons)) @@ -4010,3 +4280,23 @@ Sema::MarkDeducedTemplateParameters(FunctionTemplateDecl *FunctionTemplate, ::MarkUsedTemplateParameters(*this, Function->getParamDecl(I)->getType(), true, TemplateParams->getDepth(), Deduced); } + +bool hasDeducibleTemplateParameters(Sema &S, + FunctionTemplateDecl *FunctionTemplate, + QualType T) { + if (!T->isDependentType()) + return false; + + TemplateParameterList *TemplateParams + = FunctionTemplate->getTemplateParameters(); + llvm::SmallVector<bool, 4> Deduced; + Deduced.resize(TemplateParams->size()); + ::MarkUsedTemplateParameters(S, T, true, TemplateParams->getDepth(), + Deduced); + + for (unsigned I = 0, N = Deduced.size(); I != N; ++I) + if (Deduced[I]) + return true; + + return false; +} diff --git a/lib/Sema/SemaTemplateInstantiate.cpp b/lib/Sema/SemaTemplateInstantiate.cpp index 3c1964175b10..1988f1445814 100644 --- a/lib/Sema/SemaTemplateInstantiate.cpp +++ b/lib/Sema/SemaTemplateInstantiate.cpp @@ -62,8 +62,20 @@ Sema::getTemplateInstantiationArgs(NamedDecl *D, if (!Ctx) { Ctx = D->getDeclContext(); - assert((!D->isTemplateParameter() || !Ctx->isTranslationUnit()) && - "Template parameter doesn't have its context yet!"); + // If we have a template template parameter with translation unit context, + // then we're performing substitution into a default template argument of + // this template template parameter before we've constructed the template + // that will own this template template parameter. In this case, we + // use empty template parameter lists for all of the outer templates + // to avoid performing any substitutions. + if (Ctx->isTranslationUnit()) { + if (TemplateTemplateParmDecl *TTP + = dyn_cast<TemplateTemplateParmDecl>(D)) { + for (unsigned I = 0, N = TTP->getDepth() + 1; I != N; ++I) + Result.addOuterTemplateArguments(0, 0); + return Result; + } + } } while (!Ctx->isFileContext()) { @@ -788,6 +800,11 @@ namespace { getSema().CallsUndergoingInstantiation.pop_back(); return move(Result); } + + private: + ExprResult transformNonTypeTemplateParmRef(NonTypeTemplateParmDecl *parm, + SourceLocation loc, + const TemplateArgument &arg); }; } @@ -795,7 +812,7 @@ bool TemplateInstantiator::AlreadyTransformed(QualType T) { if (T.isNull()) return true; - if (T->isDependentType() || T->isVariablyModifiedType()) + if (T->isInstantiationDependentType() || T->isVariablyModifiedType()) return false; getSema().MarkDeclarationsReferencedInType(Loc, T); @@ -980,13 +997,14 @@ TemplateName TemplateInstantiator::TransformTemplateName(CXXScopeSpec &SS, TemplateName Template = Arg.getAsTemplate(); assert(!Template.isNull() && "Null template template argument"); - + // We don't ever want to substitute for a qualified template name, since // the qualifier is handled separately. So, look through the qualified // template name to its underlying declaration. if (QualifiedTemplateName *QTN = Template.getAsQualifiedTemplateName()) Template = TemplateName(QTN->getTemplateDecl()); - + + Template = getSema().Context.getSubstTemplateTemplateParm(TTP, Template); return Template; } } @@ -1065,46 +1083,65 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E, Arg = Arg.pack_begin()[getSema().ArgumentPackSubstitutionIndex]; } + return transformNonTypeTemplateParmRef(NTTP, E->getLocation(), Arg); +} + +ExprResult TemplateInstantiator::transformNonTypeTemplateParmRef( + NonTypeTemplateParmDecl *parm, + SourceLocation loc, + const TemplateArgument &arg) { + ExprResult result; + QualType type; + // The template argument itself might be an expression, in which // case we just return that expression. - if (Arg.getKind() == TemplateArgument::Expression) - return SemaRef.Owned(Arg.getAsExpr()); + if (arg.getKind() == TemplateArgument::Expression) { + Expr *argExpr = arg.getAsExpr(); + result = SemaRef.Owned(argExpr); + type = argExpr->getType(); - if (Arg.getKind() == TemplateArgument::Declaration) { - ValueDecl *VD = cast<ValueDecl>(Arg.getAsDecl()); + } else if (arg.getKind() == TemplateArgument::Declaration) { + ValueDecl *VD = cast<ValueDecl>(arg.getAsDecl()); // Find the instantiation of the template argument. This is // required for nested templates. VD = cast_or_null<ValueDecl>( - getSema().FindInstantiatedDecl(E->getLocation(), - VD, TemplateArgs)); + getSema().FindInstantiatedDecl(loc, VD, TemplateArgs)); if (!VD) return ExprError(); // Derive the type we want the substituted decl to have. This had // better be non-dependent, or these checks will have serious problems. - QualType TargetType; - if (NTTP->isExpandedParameterPack()) - TargetType = NTTP->getExpansionType( - getSema().ArgumentPackSubstitutionIndex); - else if (NTTP->isParameterPack() && - isa<PackExpansionType>(NTTP->getType())) { - TargetType = SemaRef.SubstType( - cast<PackExpansionType>(NTTP->getType())->getPattern(), - TemplateArgs, E->getLocation(), - NTTP->getDeclName()); - } else - TargetType = SemaRef.SubstType(NTTP->getType(), TemplateArgs, - E->getLocation(), NTTP->getDeclName()); - assert(!TargetType.isNull() && "type substitution failed for param type"); - assert(!TargetType->isDependentType() && "param type still dependent"); - return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg, - TargetType, - E->getLocation()); + if (parm->isExpandedParameterPack()) { + type = parm->getExpansionType(SemaRef.ArgumentPackSubstitutionIndex); + } else if (parm->isParameterPack() && + isa<PackExpansionType>(parm->getType())) { + type = SemaRef.SubstType( + cast<PackExpansionType>(parm->getType())->getPattern(), + TemplateArgs, loc, parm->getDeclName()); + } else { + type = SemaRef.SubstType(parm->getType(), TemplateArgs, + loc, parm->getDeclName()); + } + assert(!type.isNull() && "type substitution failed for param type"); + assert(!type->isDependentType() && "param type still dependent"); + result = SemaRef.BuildExpressionFromDeclTemplateArgument(arg, type, loc); + + if (!result.isInvalid()) type = result.get()->getType(); + } else { + result = SemaRef.BuildExpressionFromIntegralTemplateArgument(arg, loc); + + // Note that this type can be different from the type of 'result', + // e.g. if it's an enum type. + type = arg.getIntegralType(); } + if (result.isInvalid()) return ExprError(); - return SemaRef.BuildExpressionFromIntegralTemplateArgument(Arg, - E->getSourceRange().getBegin()); + Expr *resultExpr = result.take(); + return SemaRef.Owned(new (SemaRef.Context) + SubstNonTypeTemplateParmExpr(type, + resultExpr->getValueKind(), + loc, parm, resultExpr)); } ExprResult @@ -1120,36 +1157,9 @@ TemplateInstantiator::TransformSubstNonTypeTemplateParmPackExpr( assert(Index < ArgPack.pack_size() && "Substitution index out-of-range"); const TemplateArgument &Arg = ArgPack.pack_begin()[Index]; - if (Arg.getKind() == TemplateArgument::Expression) - return SemaRef.Owned(Arg.getAsExpr()); - - if (Arg.getKind() == TemplateArgument::Declaration) { - ValueDecl *VD = cast<ValueDecl>(Arg.getAsDecl()); - - // Find the instantiation of the template argument. This is - // required for nested templates. - VD = cast_or_null<ValueDecl>( - getSema().FindInstantiatedDecl(E->getParameterPackLocation(), - VD, TemplateArgs)); - if (!VD) - return ExprError(); - - QualType T; - NonTypeTemplateParmDecl *NTTP = E->getParameterPack(); - if (NTTP->isExpandedParameterPack()) - T = NTTP->getExpansionType(getSema().ArgumentPackSubstitutionIndex); - else if (const PackExpansionType *Expansion - = dyn_cast<PackExpansionType>(NTTP->getType())) - T = SemaRef.SubstType(Expansion->getPattern(), TemplateArgs, - E->getParameterPackLocation(), NTTP->getDeclName()); - else - T = E->getType(); - return SemaRef.BuildExpressionFromDeclTemplateArgument(Arg, T, - E->getParameterPackLocation()); - } - - return SemaRef.BuildExpressionFromIntegralTemplateArgument(Arg, - E->getParameterPackLocation()); + return transformNonTypeTemplateParmRef(E->getParameterPack(), + E->getParameterPackLocation(), + Arg); } ExprResult @@ -1327,7 +1337,7 @@ TypeSourceInfo *Sema::SubstType(TypeSourceInfo *T, "Cannot perform an instantiation without some context on the " "instantiation stack"); - if (!T->getType()->isDependentType() && + if (!T->getType()->isInstantiationDependentType() && !T->getType()->isVariablyModifiedType()) return T; @@ -1346,7 +1356,7 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL, if (TL.getType().isNull()) return 0; - if (!TL.getType()->isDependentType() && + if (!TL.getType()->isInstantiationDependentType() && !TL.getType()->isVariablyModifiedType()) { // FIXME: Make a copy of the TypeLoc data here, so that we can // return a new TypeSourceInfo. Inefficient! @@ -1375,7 +1385,7 @@ QualType Sema::SubstType(QualType T, // If T is not a dependent type or a variably-modified type, there // is nothing to do. - if (!T->isDependentType() && !T->isVariablyModifiedType()) + if (!T->isInstantiationDependentType() && !T->isVariablyModifiedType()) return T; TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity); @@ -1383,7 +1393,8 @@ QualType Sema::SubstType(QualType T, } static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) { - if (T->getType()->isDependentType() || T->getType()->isVariablyModifiedType()) + if (T->getType()->isInstantiationDependentType() || + T->getType()->isVariablyModifiedType()) return true; TypeLoc TL = T->getTypeLoc().IgnoreParens(); @@ -1397,7 +1408,7 @@ static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) { // The parameter's type as written might be dependent even if the // decayed type was not dependent. if (TypeSourceInfo *TSInfo = P->getTypeSourceInfo()) - if (TSInfo->getType()->isDependentType()) + if (TSInfo->getType()->isInstantiationDependentType()) return true; // TODO: currently we always rebuild expressions. When we @@ -1798,9 +1809,11 @@ Sema::InstantiateClass(SourceLocation PointOfInstantiation, ExprResult NewInit = SubstExpr(OldInit, TemplateArgs); // If the initialization is no longer dependent, check it now. - if ((OldField->getType()->isDependentType() || OldInit->isTypeDependent()) - && !NewField->getType()->isDependentType() - && !NewInit.get()->isTypeDependent()) { + if ((OldField->getType()->isDependentType() || OldInit->isTypeDependent() || + OldInit->isValueDependent()) && + !NewField->getType()->isDependentType() && + !NewInit.get()->isTypeDependent() && + !NewInit.get()->isValueDependent()) { // FIXME: handle list-initialization SourceLocation EqualLoc = NewField->getLocation(); NewInit = PerformCopyInitialization( diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index e78aa2991e74..29385e50b8c1 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -59,7 +59,7 @@ bool TemplateDeclInstantiator::SubstQualifier(const TagDecl *OldDecl, // FIXME: Is this still too simple? void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs, - Decl *Tmpl, Decl *New) { + const Decl *Tmpl, Decl *New) { for (AttrVec::const_iterator i = Tmpl->attr_begin(), e = Tmpl->attr_end(); i != e; ++i) { const Attr *TmplAttr = *i; @@ -132,7 +132,7 @@ Decl *TemplateDeclInstantiator::InstantiateTypedefNameDecl(TypedefNameDecl *D, bool IsTypeAlias) { bool Invalid = false; TypeSourceInfo *DI = D->getTypeSourceInfo(); - if (DI->getType()->isDependentType() || + if (DI->getType()->isInstantiationDependentType() || DI->getType()->isVariablyModifiedType()) { DI = SemaRef.SubstType(DI, TemplateArgs, D->getLocation(), D->getDeclName()); @@ -415,8 +415,10 @@ Decl *TemplateDeclInstantiator::VisitVarDecl(VarDecl *D) { !Var->isCXXForRangeDecl()) SemaRef.ActOnUninitializedDecl(Var, false); - // Diagnose unused local variables. - if (!Var->isInvalidDecl() && Owner->isFunctionOrMethod() && !Var->isUsed()) + // Diagnose unused local variables with dependent types, where the diagnostic + // will have been deferred. + if (!Var->isInvalidDecl() && Owner->isFunctionOrMethod() && !Var->isUsed() && + D->getType()->isDependentType()) SemaRef.DiagnoseUnusedDecl(Var); return Var; @@ -433,7 +435,7 @@ Decl *TemplateDeclInstantiator::VisitAccessSpecDecl(AccessSpecDecl *D) { Decl *TemplateDeclInstantiator::VisitFieldDecl(FieldDecl *D) { bool Invalid = false; TypeSourceInfo *DI = D->getTypeSourceInfo(); - if (DI->getType()->isDependentType() || + if (DI->getType()->isInstantiationDependentType() || DI->getType()->isVariablyModifiedType()) { DI = SemaRef.SubstType(DI, TemplateArgs, D->getLocation(), D->getDeclName()); @@ -1088,9 +1090,26 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D, Function->setLexicalDeclContext(LexicalDC); // Attach the parameters - for (unsigned P = 0; P < Params.size(); ++P) - if (Params[P]) - Params[P]->setOwningFunction(Function); + if (isa<FunctionProtoType>(Function->getType().IgnoreParens())) { + // Adopt the already-instantiated parameters into our own context. + for (unsigned P = 0; P < Params.size(); ++P) + if (Params[P]) + Params[P]->setOwningFunction(Function); + } else { + // Since we were instantiated via a typedef of a function type, create + // new parameters. + const FunctionProtoType *Proto + = Function->getType()->getAs<FunctionProtoType>(); + assert(Proto && "No function prototype in template instantiation?"); + for (FunctionProtoType::arg_type_iterator AI = Proto->arg_type_begin(), + AE = Proto->arg_type_end(); AI != AE; ++AI) { + ParmVarDecl *Param + = SemaRef.BuildParmVarDeclForTypedef(Function, Function->getLocation(), + *AI); + Param->setScopeInfo(0, Params.size()); + Params.push_back(Param); + } + } Function->setParams(Params.data(), Params.size()); SourceLocation InstantiateAtPOI; @@ -2264,7 +2283,12 @@ TemplateDeclInstantiator::InitFunctionInstantiation(FunctionDecl *New, EPI)); } - SemaRef.InstantiateAttrs(TemplateArgs, Tmpl, New); + const FunctionDecl* Definition = Tmpl; + + // Get the definition. Leaves the variable unchanged if undefined. + Tmpl->isDefined(Definition); + + SemaRef.InstantiateAttrs(TemplateArgs, Definition, New); return false; } diff --git a/lib/Sema/SemaTemplateVariadic.cpp b/lib/Sema/SemaTemplateVariadic.cpp index 86d3bc170909..daa1523363c0 100644 --- a/lib/Sema/SemaTemplateVariadic.cpp +++ b/lib/Sema/SemaTemplateVariadic.cpp @@ -716,17 +716,19 @@ ExprResult Sema::ActOnSizeofParameterPackExpr(Scope *S, case LookupResult::NotFound: case LookupResult::NotFoundInCurrentInstantiation: - if (DeclarationName CorrectedName = CorrectTypo(R, S, 0, 0, false, - CTC_NoKeywords)) { - if (NamedDecl *CorrectedResult = R.getAsSingle<NamedDecl>()) + if (TypoCorrection Corrected = CorrectTypo(R.getLookupNameInfo(), + R.getLookupKind(), S, 0, 0, + false, CTC_NoKeywords)) { + if (NamedDecl *CorrectedResult = Corrected.getCorrectionDecl()) if (CorrectedResult->isParameterPack()) { + std::string CorrectedQuotedStr(Corrected.getQuoted(getLangOptions())); ParameterPack = CorrectedResult; Diag(NameLoc, diag::err_sizeof_pack_no_pack_name_suggest) - << &Name << CorrectedName - << FixItHint::CreateReplacement(NameLoc, - CorrectedName.getAsString()); + << &Name << CorrectedQuotedStr + << FixItHint::CreateReplacement( + NameLoc, Corrected.getAsString(getLangOptions())); Diag(ParameterPack->getLocation(), diag::note_parameter_pack_here) - << CorrectedName; + << CorrectedQuotedStr; } } diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index 5fd8afa6acf4..f3e73ec5a723 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -26,36 +26,11 @@ #include "clang/Basic/TargetInfo.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/DeclSpec.h" +#include "clang/Sema/DelayedDiagnostic.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/ErrorHandling.h" using namespace clang; -/// \brief Perform adjustment on the parameter type of a function. -/// -/// This routine adjusts the given parameter type @p T to the actual -/// parameter type used by semantic analysis (C99 6.7.5.3p[7,8], -/// C++ [dcl.fct]p3). The adjusted parameter type is returned. -QualType Sema::adjustParameterType(QualType T) { - // C99 6.7.5.3p7: - // A declaration of a parameter as "array of type" shall be - // adjusted to "qualified pointer to type", where the type - // qualifiers (if any) are those specified within the [ and ] of - // the array type derivation. - if (T->isArrayType()) - return Context.getArrayDecayedType(T); - - // C99 6.7.5.3p8: - // A declaration of a parameter as "function returning type" - // shall be adjusted to "pointer to function returning type", as - // in 6.3.2.1. - if (T->isFunctionType()) - return Context.getPointerType(T); - - return T; -} - - - /// isOmittedBlockReturnType - Return true if this declarator is missing a /// return type because this is a omitted return type on a block literal. static bool isOmittedBlockReturnType(const Declarator &D) { @@ -86,6 +61,11 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, useInstantiationLoc = true; break; + case AttributeList::AT_objc_ownership: + diagID = diag::warn_objc_object_attribute_wrong_type; + useInstantiationLoc = true; + break; + default: // Assume everything else was a function attribute. diagID = diag::warn_function_attribute_wrong_type; @@ -110,7 +90,8 @@ static void diagnoseBadTypeAttribute(Sema &S, const AttributeList &attr, // objc_gc applies to Objective-C pointers or, otherwise, to the // smallest available pointer type (i.e. 'void*' in 'void**'). #define OBJC_POINTER_TYPE_ATTRS_CASELIST \ - case AttributeList::AT_objc_gc + case AttributeList::AT_objc_gc: \ + case AttributeList::AT_objc_ownership // Function type attributes. #define FUNCTION_TYPE_ATTRS_CASELIST \ @@ -295,11 +276,15 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, static bool handleObjCGCTypeAttr(TypeProcessingState &state, AttributeList &attr, QualType &type); +static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, + AttributeList &attr, QualType &type); + static bool handleObjCPointerTypeAttr(TypeProcessingState &state, AttributeList &attr, QualType &type) { - // Right now, we have exactly one of these attributes: objc_gc. - assert(attr.getKind() == AttributeList::AT_objc_gc); - return handleObjCGCTypeAttr(state, attr, type); + if (attr.getKind() == AttributeList::AT_objc_gc) + return handleObjCGCTypeAttr(state, attr, type); + assert(attr.getKind() == AttributeList::AT_objc_ownership); + return handleObjCOwnershipTypeAttr(state, attr, type); } /// Given that an objc_gc attribute was written somewhere on a @@ -447,7 +432,12 @@ distributeFunctionTypeAttrToInnermost(TypeProcessingState &state, return true; } - return handleFunctionTypeAttr(state, attr, declSpecType); + if (handleFunctionTypeAttr(state, attr, declSpecType)) { + spliceAttrOutOfList(attr, attrList); + return true; + } + + return false; } /// A function type attribute was written in the decl spec. Try to @@ -512,6 +502,11 @@ static void distributeTypeAttrsFromDeclarator(TypeProcessingState &state, distributeObjCPointerTypeAttrFromDeclarator(state, *attr, declSpecType); break; + case AttributeList::AT_ns_returns_retained: + if (!state.getSema().getLangOptions().ObjCAutoRefCount) + break; + // fallthrough + FUNCTION_TYPE_ATTRS_CASELIST: distributeFunctionTypeAttrFromDeclarator(state, *attr, declSpecType); break; @@ -560,6 +555,7 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state, /*args*/ 0, 0, /*type quals*/ 0, /*ref-qualifier*/true, SourceLocation(), + /*mutable qualifier*/SourceLocation(), /*EH*/ EST_None, SourceLocation(), 0, 0, 0, 0, /*parens*/ loc, loc, declarator)); @@ -575,10 +571,11 @@ static void maybeSynthesizeBlockSignature(TypeProcessingState &state, /// \param D the declarator containing the declaration specifier. /// \returns The type described by the declaration specifiers. This function /// never returns null. -static QualType ConvertDeclSpecToType(Sema &S, TypeProcessingState &state) { +static QualType ConvertDeclSpecToType(TypeProcessingState &state) { // FIXME: Should move the logic from DeclSpec::Finish to here for validity // checking. + Sema &S = state.getSema(); Declarator &declarator = state.getDeclarator(); const DeclSpec &DS = declarator.getDeclSpec(); SourceLocation DeclLoc = declarator.getIdentifierLoc(); @@ -1017,6 +1014,51 @@ QualType Sema::BuildParenType(QualType T) { return Context.getParenType(T); } +/// Given that we're building a pointer or reference to the given +static QualType inferARCLifetimeForPointee(Sema &S, QualType type, + SourceLocation loc, + bool isReference) { + // Bail out if retention is unrequired or already specified. + if (!type->isObjCLifetimeType() || + type.getObjCLifetime() != Qualifiers::OCL_None) + return type; + + Qualifiers::ObjCLifetime implicitLifetime = Qualifiers::OCL_None; + + // If the object type is const-qualified, we can safely use + // __unsafe_unretained. This is safe (because there are no read + // barriers), and it'll be safe to coerce anything but __weak* to + // the resulting type. + if (type.isConstQualified()) { + implicitLifetime = Qualifiers::OCL_ExplicitNone; + + // Otherwise, check whether the static type does not require + // retaining. This currently only triggers for Class (possibly + // protocol-qualifed, and arrays thereof). + } else if (type->isObjCARCImplicitlyUnretainedType()) { + implicitLifetime = Qualifiers::OCL_ExplicitNone; + + // If that failed, give an error and recover using __autoreleasing. + } else { + // These types can show up in private ivars in system headers, so + // we need this to not be an error in those cases. Instead we + // want to delay. + if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { + S.DelayedDiagnostics.add( + sema::DelayedDiagnostic::makeForbiddenType(loc, + diag::err_arc_indirect_no_ownership, type, isReference)); + } else { + S.Diag(loc, diag::err_arc_indirect_no_ownership) << type << isReference; + } + implicitLifetime = Qualifiers::OCL_Autoreleasing; + } + assert(implicitLifetime && "didn't infer any lifetime!"); + + Qualifiers qs; + qs.addObjCLifetime(implicitLifetime); + return S.Context.getQualifiedType(type, qs); +} + /// \brief Build a pointer type. /// /// \param T The type to which we'll be building a pointer. @@ -1041,6 +1083,10 @@ QualType Sema::BuildPointerType(QualType T, assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType"); + // In ARC, it is forbidden to build pointers to unqualified pointers. + if (getLangOptions().ObjCAutoRefCount) + T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ false); + // Build the pointer type. return Context.getPointerType(T); } @@ -1094,12 +1140,38 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue, return QualType(); } + // In ARC, it is forbidden to build references to unqualified pointers. + if (getLangOptions().ObjCAutoRefCount) + T = inferARCLifetimeForPointee(*this, T, Loc, /*reference*/ true); + // Handle restrict on references. if (LValueRef) return Context.getLValueReferenceType(T, SpelledAsLValue); return Context.getRValueReferenceType(T); } +/// Check whether the specified array size makes the array type a VLA. If so, +/// return true, if not, return the size of the array in SizeVal. +static bool isArraySizeVLA(Expr *ArraySize, llvm::APSInt &SizeVal, Sema &S) { + // If the size is an ICE, it certainly isn't a VLA. + if (ArraySize->isIntegerConstantExpr(SizeVal, S.Context)) + return false; + + // If we're in a GNU mode (like gnu99, but not c99) accept any evaluatable + // value as an extension. + Expr::EvalResult Result; + if (S.LangOpts.GNUMode && ArraySize->Evaluate(Result, S.Context)) { + if (!Result.hasSideEffects() && Result.Val.isInt()) { + SizeVal = Result.Val.getInt(); + S.Diag(ArraySize->getLocStart(), diag::ext_vla_folded_to_constant); + return false; + } + } + + return true; +} + + /// \brief Build an array type. /// /// \param T The type of each element in the array. @@ -1200,11 +1272,13 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, T = Context.getIncompleteArrayType(T, ASM, Quals); } else if (ArraySize->isTypeDependent() || ArraySize->isValueDependent()) { T = Context.getDependentSizedArrayType(T, ArraySize, ASM, Quals, Brackets); - } else if (!ArraySize->isIntegerConstantExpr(ConstVal, Context) || - (!T->isDependentType() && !T->isIncompleteType() && - !T->isConstantSizeType())) { - // Per C99, a variable array is an array with either a non-constant - // size or an element type that has a non-constant-size + } else if (!T->isDependentType() && !T->isIncompleteType() && + !T->isConstantSizeType()) { + // C99: an array with an element type that has a non-constant-size is a VLA. + T = Context.getVariableArrayType(T, ArraySize, ASM, Quals, Brackets); + } else if (isArraySizeVLA(ArraySize, ConstVal, *this)) { + // C99: an array with a non-ICE size is a VLA. We accept any expression + // that we can fold to a non-zero positive value as an extension. T = Context.getVariableArrayType(T, ArraySize, ASM, Quals, Brackets); } else { // C99 6.7.5.2p1: If the expression is a constant expression, it shall @@ -1242,10 +1316,12 @@ QualType Sema::BuildArrayType(QualType T, ArrayType::ArraySizeModifier ASM, if (!getLangOptions().C99) { if (T->isVariableArrayType()) { // Prohibit the use of non-POD types in VLAs. + QualType BaseT = Context.getBaseElementType(T); if (!T->isDependentType() && - !Context.getBaseElementType(T)->isPODType()) { + !BaseT.isPODType(Context) && + !BaseT->isObjCLifetimeType()) { Diag(Loc, diag::err_vla_non_pod) - << Context.getBaseElementType(T); + << BaseT; return QualType(); } // Prohibit the use of VLAs during template argument deduction. @@ -1296,8 +1372,7 @@ QualType Sema::BuildExtVectorType(QualType T, Expr *ArraySize, return QualType(); } - if (!T->isDependentType()) - return Context.getExtVectorType(T, vectorSize); + return Context.getExtVectorType(T, vectorSize); } return Context.getDependentSizedExtVectorType(T, ArraySize, AttrLoc); @@ -1347,7 +1422,7 @@ QualType Sema::BuildFunctionType(QualType T, bool Invalid = false; for (unsigned Idx = 0; Idx < NumParamTypes; ++Idx) { - QualType ParamType = adjustParameterType(ParamTypes[Idx]); + QualType ParamType = Context.getAdjustedParameterType(ParamTypes[Idx]); if (ParamType->isVoidType()) { Diag(Loc, diag::err_param_with_void_type); Invalid = true; @@ -1467,6 +1542,109 @@ QualType Sema::GetTypeFromParser(ParsedType Ty, TypeSourceInfo **TInfo) { return QT; } +static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state, + Qualifiers::ObjCLifetime ownership, + unsigned chunkIndex); + +/// Given that this is the declaration of a parameter under ARC, +/// attempt to infer attributes and such for pointer-to-whatever +/// types. +static void inferARCWriteback(TypeProcessingState &state, + QualType &declSpecType) { + Sema &S = state.getSema(); + Declarator &declarator = state.getDeclarator(); + + // TODO: should we care about decl qualifiers? + + // Check whether the declarator has the expected form. We walk + // from the inside out in order to make the block logic work. + unsigned outermostPointerIndex = 0; + bool isBlockPointer = false; + unsigned numPointers = 0; + for (unsigned i = 0, e = declarator.getNumTypeObjects(); i != e; ++i) { + unsigned chunkIndex = i; + DeclaratorChunk &chunk = declarator.getTypeObject(chunkIndex); + switch (chunk.Kind) { + case DeclaratorChunk::Paren: + // Ignore parens. + break; + + case DeclaratorChunk::Reference: + case DeclaratorChunk::Pointer: + // Count the number of pointers. Treat references + // interchangeably as pointers; if they're mis-ordered, normal + // type building will discover that. + outermostPointerIndex = chunkIndex; + numPointers++; + break; + + case DeclaratorChunk::BlockPointer: + // If we have a pointer to block pointer, that's an acceptable + // indirect reference; anything else is not an application of + // the rules. + if (numPointers != 1) return; + numPointers++; + outermostPointerIndex = chunkIndex; + isBlockPointer = true; + + // We don't care about pointer structure in return values here. + goto done; + + case DeclaratorChunk::Array: // suppress if written (id[])? + case DeclaratorChunk::Function: + case DeclaratorChunk::MemberPointer: + return; + } + } + done: + + // If we have *one* pointer, then we want to throw the qualifier on + // the declaration-specifiers, which means that it needs to be a + // retainable object type. + if (numPointers == 1) { + // If it's not a retainable object type, the rule doesn't apply. + if (!declSpecType->isObjCRetainableType()) return; + + // If it already has lifetime, don't do anything. + if (declSpecType.getObjCLifetime()) return; + + // Otherwise, modify the type in-place. + Qualifiers qs; + + if (declSpecType->isObjCARCImplicitlyUnretainedType()) + qs.addObjCLifetime(Qualifiers::OCL_ExplicitNone); + else + qs.addObjCLifetime(Qualifiers::OCL_Autoreleasing); + declSpecType = S.Context.getQualifiedType(declSpecType, qs); + + // If we have *two* pointers, then we want to throw the qualifier on + // the outermost pointer. + } else if (numPointers == 2) { + // If we don't have a block pointer, we need to check whether the + // declaration-specifiers gave us something that will turn into a + // retainable object pointer after we slap the first pointer on it. + if (!isBlockPointer && !declSpecType->isObjCObjectType()) + return; + + // Look for an explicit lifetime attribute there. + DeclaratorChunk &chunk = declarator.getTypeObject(outermostPointerIndex); + if (chunk.Kind != DeclaratorChunk::Pointer && + chunk.Kind != DeclaratorChunk::BlockPointer) + return; + for (const AttributeList *attr = chunk.getAttrs(); attr; + attr = attr->getNext()) + if (attr->getKind() == AttributeList::AT_objc_ownership) + return; + + transferARCOwnershipToDeclaratorChunk(state, Qualifiers::OCL_Autoreleasing, + outermostPointerIndex); + + // Any other number of pointers/references does not trigger the rule. + } else return; + + // TODO: mark whether we did this inference? +} + static void DiagnoseIgnoredQualifiers(unsigned Quals, SourceLocation ConstQualLoc, SourceLocation VolatileQualLoc, @@ -1513,47 +1691,28 @@ static void DiagnoseIgnoredQualifiers(unsigned Quals, << QualStr << NumQuals << ConstFixIt << VolatileFixIt << RestrictFixIt; } -/// GetTypeForDeclarator - Convert the type for the specified -/// declarator to Type instances. -/// -/// If OwnedDecl is non-NULL, and this declarator's decl-specifier-seq -/// owns the declaration of a type (e.g., the definition of a struct -/// type), then *OwnedDecl will receive the owned declaration. -/// -/// The result of this call will never be null, but the associated -/// type may be a null type if there's an unrecoverable error. -TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, - TagDecl **OwnedDecl, - bool AutoAllowedInTypeName) { - // Determine the type of the declarator. Not all forms of declarator - // have a type. +static QualType GetDeclSpecTypeForDeclarator(TypeProcessingState &state, + TypeSourceInfo *&ReturnTypeInfo) { + Sema &SemaRef = state.getSema(); + Declarator &D = state.getDeclarator(); QualType T; - TypeSourceInfo *ReturnTypeInfo = 0; - - TypeProcessingState state(*this, D); + ReturnTypeInfo = 0; - // In C++0x, deallocation functions (normal and array operator delete) - // are implicitly noexcept. - bool ImplicitlyNoexcept = false; + // The TagDecl owned by the DeclSpec. + TagDecl *OwnedTagDecl = 0; switch (D.getName().getKind()) { + case UnqualifiedId::IK_ImplicitSelfParam: case UnqualifiedId::IK_OperatorFunctionId: - if (getLangOptions().CPlusPlus0x) { - OverloadedOperatorKind OO = D.getName().OperatorFunctionId.Operator; - if (OO == OO_Delete || OO == OO_Array_Delete) - ImplicitlyNoexcept = true; - } - // Intentional fall-through. case UnqualifiedId::IK_Identifier: case UnqualifiedId::IK_LiteralOperatorId: case UnqualifiedId::IK_TemplateId: - T = ConvertDeclSpecToType(*this, state); + T = ConvertDeclSpecToType(state); if (!D.isInvalidType() && D.getDeclSpec().isTypeSpecOwned()) { - TagDecl* Owned = cast<TagDecl>(D.getDeclSpec().getRepAsDecl()); + OwnedTagDecl = cast<TagDecl>(D.getDeclSpec().getRepAsDecl()); // Owned declaration is embedded in declarator. - Owned->setEmbeddedInDeclarator(true); - if (OwnedDecl) *OwnedDecl = Owned; + OwnedTagDecl->setEmbeddedInDeclarator(true); } break; @@ -1562,14 +1721,14 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, case UnqualifiedId::IK_DestructorName: // Constructors and destructors don't have return types. Use // "void" instead. - T = Context.VoidTy; + T = SemaRef.Context.VoidTy; break; case UnqualifiedId::IK_ConversionFunctionId: // The result type of a conversion function is the type that it // converts to. - T = GetTypeFromParser(D.getName().ConversionFunctionId, - &ReturnTypeInfo); + T = SemaRef.GetTypeFromParser(D.getName().ConversionFunctionId, + &ReturnTypeInfo); break; } @@ -1581,7 +1740,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // type (this is checked later) and we can skip this. In other languages // using auto, we need to check regardless. if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && - (!getLangOptions().CPlusPlus0x || !D.isFunctionDeclarator())) { + (!SemaRef.getLangOptions().CPlusPlus0x || !D.isFunctionDeclarator())) { int Error = -1; switch (D.getContext()) { @@ -1595,7 +1754,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, case Declarator::MemberContext: if (D.getDeclSpec().getStorageClassSpec() == DeclSpec::SCS_static) break; - switch (cast<TagDecl>(CurContext)->getTagKind()) { + switch (cast<TagDecl>(SemaRef.CurContext)->getTagKind()) { case TTK_Enum: assert(0 && "unhandled tag kind"); break; case TTK_Struct: Error = 1; /* Struct member */ break; case TTK_Union: Error = 2; /* Union member */ break; @@ -1603,6 +1762,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } break; case Declarator::CXXCatchContext: + case Declarator::ObjCCatchContext: Error = 4; // Exception declaration break; case Declarator::TemplateParamContext: @@ -1619,13 +1779,13 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, Error = 9; // Type alias break; case Declarator::TypeNameContext: - if (!AutoAllowedInTypeName) - Error = 11; // Generic + Error = 11; // Generic break; case Declarator::FileContext: case Declarator::BlockContext: case Declarator::ForContext: case Declarator::ConditionContext: + case Declarator::CXXNewContext: break; } @@ -1640,7 +1800,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // contains a trailing return type. That is only legal at the outermost // level. Check all declarator chunks (outermost first) anyway, to give // better diagnostics. - if (getLangOptions().CPlusPlus0x && Error != -1) { + if (SemaRef.getLangOptions().CPlusPlus0x && Error != -1) { for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { unsigned chunkIndex = e - i - 1; state.setCurrentChunkIndex(chunkIndex); @@ -1656,15 +1816,86 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } if (Error != -1) { - Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_auto_not_allowed) + SemaRef.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), + diag::err_auto_not_allowed) << Error; - T = Context.IntTy; + T = SemaRef.Context.IntTy; D.setInvalidType(true); } } - - if (T.isNull()) - return Context.getNullTypeSourceInfo(); + + if (SemaRef.getLangOptions().CPlusPlus && + OwnedTagDecl && OwnedTagDecl->isDefinition()) { + // Check the contexts where C++ forbids the declaration of a new class + // or enumeration in a type-specifier-seq. + switch (D.getContext()) { + case Declarator::FileContext: + case Declarator::MemberContext: + case Declarator::BlockContext: + case Declarator::ForContext: + case Declarator::BlockLiteralContext: + // C++0x [dcl.type]p3: + // A type-specifier-seq shall not define a class or enumeration unless + // it appears in the type-id of an alias-declaration (7.1.3) that is not + // the declaration of a template-declaration. + case Declarator::AliasDeclContext: + break; + case Declarator::AliasTemplateContext: + SemaRef.Diag(OwnedTagDecl->getLocation(), + diag::err_type_defined_in_alias_template) + << SemaRef.Context.getTypeDeclType(OwnedTagDecl); + break; + case Declarator::TypeNameContext: + case Declarator::TemplateParamContext: + case Declarator::CXXNewContext: + case Declarator::CXXCatchContext: + case Declarator::ObjCCatchContext: + case Declarator::TemplateTypeArgContext: + SemaRef.Diag(OwnedTagDecl->getLocation(), + diag::err_type_defined_in_type_specifier) + << SemaRef.Context.getTypeDeclType(OwnedTagDecl); + break; + case Declarator::PrototypeContext: + case Declarator::ObjCPrototypeContext: + case Declarator::KNRTypeListContext: + // C++ [dcl.fct]p6: + // Types shall not be defined in return or parameter types. + SemaRef.Diag(OwnedTagDecl->getLocation(), + diag::err_type_defined_in_param_type) + << SemaRef.Context.getTypeDeclType(OwnedTagDecl); + break; + case Declarator::ConditionContext: + // C++ 6.4p2: + // The type-specifier-seq shall not contain typedef and shall not declare + // a new class or enumeration. + SemaRef.Diag(OwnedTagDecl->getLocation(), + diag::err_type_defined_in_condition); + break; + } + } + + return T; +} + +static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, + QualType declSpecType, + TypeSourceInfo *TInfo) { + + QualType T = declSpecType; + Declarator &D = state.getDeclarator(); + Sema &S = state.getSema(); + ASTContext &Context = S.Context; + const LangOptions &LangOpts = S.getLangOptions(); + + bool ImplicitlyNoexcept = false; + if (D.getName().getKind() == UnqualifiedId::IK_OperatorFunctionId && + LangOpts.CPlusPlus0x) { + OverloadedOperatorKind OO = D.getName().OperatorFunctionId.Operator; + /// In C++0x, deallocation functions (normal and array operator delete) + /// are implicitly noexcept. + if (OO == OO_Delete || OO == OO_Array_Delete) + ImplicitlyNoexcept = true; + } // The name we're declaring, if any. DeclarationName Name; @@ -1687,56 +1918,56 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, switch (DeclType.Kind) { default: assert(0 && "Unknown decltype!"); case DeclaratorChunk::Paren: - T = BuildParenType(T); + T = S.BuildParenType(T); break; case DeclaratorChunk::BlockPointer: // If blocks are disabled, emit an error. if (!LangOpts.Blocks) - Diag(DeclType.Loc, diag::err_blocks_disable); + S.Diag(DeclType.Loc, diag::err_blocks_disable); - T = BuildBlockPointerType(T, D.getIdentifierLoc(), Name); + T = S.BuildBlockPointerType(T, D.getIdentifierLoc(), Name); if (DeclType.Cls.TypeQuals) - T = BuildQualifiedType(T, DeclType.Loc, DeclType.Cls.TypeQuals); + T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Cls.TypeQuals); break; case DeclaratorChunk::Pointer: // Verify that we're not building a pointer to pointer to function with // exception specification. - if (getLangOptions().CPlusPlus && CheckDistantExceptionSpec(T)) { - Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); + if (LangOpts.CPlusPlus && S.CheckDistantExceptionSpec(T)) { + S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); D.setInvalidType(true); // Build the type anyway. } - if (getLangOptions().ObjC1 && T->getAs<ObjCObjectType>()) { + if (LangOpts.ObjC1 && T->getAs<ObjCObjectType>()) { T = Context.getObjCObjectPointerType(T); if (DeclType.Ptr.TypeQuals) - T = BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals); + T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals); break; } - T = BuildPointerType(T, DeclType.Loc, Name); + T = S.BuildPointerType(T, DeclType.Loc, Name); if (DeclType.Ptr.TypeQuals) - T = BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals); + T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Ptr.TypeQuals); break; case DeclaratorChunk::Reference: { // Verify that we're not building a reference to pointer to function with // exception specification. - if (getLangOptions().CPlusPlus && CheckDistantExceptionSpec(T)) { - Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); + if (LangOpts.CPlusPlus && S.CheckDistantExceptionSpec(T)) { + S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); D.setInvalidType(true); // Build the type anyway. } - T = BuildReferenceType(T, DeclType.Ref.LValueRef, DeclType.Loc, Name); + T = S.BuildReferenceType(T, DeclType.Ref.LValueRef, DeclType.Loc, Name); Qualifiers Quals; if (DeclType.Ref.HasRestrict) - T = BuildQualifiedType(T, DeclType.Loc, Qualifiers::Restrict); + T = S.BuildQualifiedType(T, DeclType.Loc, Qualifiers::Restrict); break; } case DeclaratorChunk::Array: { // Verify that we're not building an array of pointers to function with // exception specification. - if (getLangOptions().CPlusPlus && CheckDistantExceptionSpec(T)) { - Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); + if (LangOpts.CPlusPlus && S.CheckDistantExceptionSpec(T)) { + S.Diag(D.getIdentifierLoc(), diag::err_distant_exception_spec); D.setInvalidType(true); // Build the type anyway. } @@ -1753,13 +1984,13 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // FIXME: This check isn't quite right: it allows star in prototypes // for function definitions, and disallows some edge cases detailed // in http://gcc.gnu.org/ml/gcc-patches/2009-02/msg00133.html - Diag(DeclType.Loc, diag::err_array_star_outside_prototype); + S.Diag(DeclType.Loc, diag::err_array_star_outside_prototype); ASM = ArrayType::Normal; D.setInvalidType(true); } - T = BuildArrayType(T, ASM, ArraySize, - Qualifiers::fromCVRMask(ATI.TypeQuals), - SourceRange(DeclType.Loc, DeclType.EndLoc), Name); + T = S.BuildArrayType(T, ASM, ArraySize, + Qualifiers::fromCVRMask(ATI.TypeQuals), + SourceRange(DeclType.Loc, DeclType.EndLoc), Name); break; } case DeclaratorChunk::Function: { @@ -1775,27 +2006,27 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // and not, for instance, a pointer to a function. if (D.getDeclSpec().getTypeSpecType() == DeclSpec::TST_auto && !FTI.TrailingReturnType && chunkIndex == 0) { - Diag(D.getDeclSpec().getTypeSpecTypeLoc(), + S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_auto_missing_trailing_return); T = Context.IntTy; D.setInvalidType(true); } else if (FTI.TrailingReturnType) { // T must be exactly 'auto' at this point. See CWG issue 681. if (isa<ParenType>(T)) { - Diag(D.getDeclSpec().getTypeSpecTypeLoc(), + S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_in_parens) << T << D.getDeclSpec().getSourceRange(); D.setInvalidType(true); } else if (T.hasQualifiers() || !isa<AutoType>(T)) { - Diag(D.getDeclSpec().getTypeSpecTypeLoc(), + S.Diag(D.getDeclSpec().getTypeSpecTypeLoc(), diag::err_trailing_return_without_auto) << T << D.getDeclSpec().getSourceRange(); D.setInvalidType(true); } - T = GetTypeFromParser( + T = S.GetTypeFromParser( ParsedType::getFromOpaquePtr(FTI.TrailingReturnType), - &ReturnTypeInfo); + &TInfo); } } @@ -1809,7 +2040,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, if (chunkIndex == 0 && D.getContext() == Declarator::BlockLiteralContext) diagID = diag::err_block_returning_array_function; - Diag(DeclType.Loc, diagID) << T->isFunctionType() << T; + S.Diag(DeclType.Loc, diagID) << T->isFunctionType() << T; T = Context.IntTy; D.setInvalidType(true); } @@ -1818,7 +2049,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // class type in C++. if (isa<PointerType>(T) && T.getLocalCVRQualifiers() && (D.getName().getKind() != UnqualifiedId::IK_ConversionFunctionId) && - (!getLangOptions().CPlusPlus || !T->isDependentType())) { + (!LangOpts.CPlusPlus || !T->isDependentType())) { assert(chunkIndex + 1 < e && "No DeclaratorChunk for the return type?"); DeclaratorChunk ReturnTypeChunk = D.getTypeObject(chunkIndex + 1); assert(ReturnTypeChunk.Kind == DeclaratorChunk::Pointer); @@ -1829,43 +2060,43 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, SourceLocation::getFromRawEncoding(PTI.ConstQualLoc), SourceLocation::getFromRawEncoding(PTI.VolatileQualLoc), SourceLocation::getFromRawEncoding(PTI.RestrictQualLoc), - *this); + S); } else if (T.getCVRQualifiers() && D.getDeclSpec().getTypeQualifiers() && - (!getLangOptions().CPlusPlus || + (!LangOpts.CPlusPlus || (!T->isDependentType() && !T->isRecordType()))) { DiagnoseIgnoredQualifiers(D.getDeclSpec().getTypeQualifiers(), D.getDeclSpec().getConstSpecLoc(), D.getDeclSpec().getVolatileSpecLoc(), D.getDeclSpec().getRestrictSpecLoc(), - *this); + S); } - if (getLangOptions().CPlusPlus && D.getDeclSpec().isTypeSpecOwned()) { + if (LangOpts.CPlusPlus && D.getDeclSpec().isTypeSpecOwned()) { // C++ [dcl.fct]p6: // Types shall not be defined in return or parameter types. TagDecl *Tag = cast<TagDecl>(D.getDeclSpec().getRepAsDecl()); if (Tag->isDefinition()) - Diag(Tag->getLocation(), diag::err_type_defined_in_result_type) + S.Diag(Tag->getLocation(), diag::err_type_defined_in_result_type) << Context.getTypeDeclType(Tag); } // Exception specs are not allowed in typedefs. Complain, but add it // anyway. if (IsTypedefName && FTI.getExceptionSpecType()) - Diag(FTI.getExceptionSpecLoc(), diag::err_exception_spec_in_typedef) + S.Diag(FTI.getExceptionSpecLoc(), diag::err_exception_spec_in_typedef) << (D.getContext() == Declarator::AliasDeclContext || D.getContext() == Declarator::AliasTemplateContext); - if (!FTI.NumArgs && !FTI.isVariadic && !getLangOptions().CPlusPlus) { + if (!FTI.NumArgs && !FTI.isVariadic && !LangOpts.CPlusPlus) { // Simple void foo(), where the incoming T is the result type. T = Context.getFunctionNoProtoType(T); } else { // We allow a zero-parameter variadic function in C if the // function is marked with the "overloadable" attribute. Scan // for this attribute now. - if (!FTI.NumArgs && FTI.isVariadic && !getLangOptions().CPlusPlus) { + if (!FTI.NumArgs && FTI.isVariadic && !LangOpts.CPlusPlus) { bool Overloadable = false; for (const AttributeList *Attrs = D.getAttributes(); Attrs; Attrs = Attrs->getNext()) { @@ -1876,13 +2107,13 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } if (!Overloadable) - Diag(FTI.getEllipsisLoc(), diag::err_ellipsis_first_arg); + S.Diag(FTI.getEllipsisLoc(), diag::err_ellipsis_first_arg); } if (FTI.NumArgs && FTI.ArgInfo[0].Param == 0) { // C99 6.7.5.3p3: Reject int(x,y,z) when it's not a function // definition. - Diag(FTI.ArgInfo[0].IdentLoc, diag::err_ident_list_in_fn_declaration); + S.Diag(FTI.ArgInfo[0].IdentLoc, diag::err_ident_list_in_fn_declaration); D.setInvalidType(true); break; } @@ -1899,13 +2130,18 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, llvm::SmallVector<QualType, 16> ArgTys; ArgTys.reserve(FTI.NumArgs); + llvm::SmallVector<bool, 16> ConsumedArguments; + ConsumedArguments.reserve(FTI.NumArgs); + bool HasAnyConsumedArguments = false; + for (unsigned i = 0, e = FTI.NumArgs; i != e; ++i) { ParmVarDecl *Param = cast<ParmVarDecl>(FTI.ArgInfo[i].Param); QualType ArgTy = Param->getType(); assert(!ArgTy.isNull() && "Couldn't parse type?"); // Adjust the parameter type. - assert((ArgTy == adjustParameterType(ArgTy)) && "Unadjusted type?"); + assert((ArgTy == Context.getAdjustedParameterType(ArgTy)) && + "Unadjusted type?"); // Look for 'void'. void is allowed only as a single argument to a // function with no other parameters (C99 6.7.5.3p10). We record @@ -1915,19 +2151,19 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // is an incomplete type (C99 6.2.5p19) and function decls cannot // have arguments of incomplete type. if (FTI.NumArgs != 1 || FTI.isVariadic) { - Diag(DeclType.Loc, diag::err_void_only_param); + S.Diag(DeclType.Loc, diag::err_void_only_param); ArgTy = Context.IntTy; Param->setType(ArgTy); } else if (FTI.ArgInfo[i].Ident) { // Reject, but continue to parse 'int(void abc)'. - Diag(FTI.ArgInfo[i].IdentLoc, + S.Diag(FTI.ArgInfo[i].IdentLoc, diag::err_param_with_void_type); ArgTy = Context.IntTy; Param->setType(ArgTy); } else { // Reject, but continue to parse 'float(const void)'. if (ArgTy.hasQualifiers()) - Diag(DeclType.Loc, diag::err_void_param_qualified); + S.Diag(DeclType.Loc, diag::err_void_param_qualified); // Do not add 'void' to the ArgTys list. break; @@ -1944,19 +2180,28 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } } + if (LangOpts.ObjCAutoRefCount) { + bool Consumed = Param->hasAttr<NSConsumedAttr>(); + ConsumedArguments.push_back(Consumed); + HasAnyConsumedArguments |= Consumed; + } + ArgTys.push_back(ArgTy); } + if (HasAnyConsumedArguments) + EPI.ConsumedArguments = ConsumedArguments.data(); + llvm::SmallVector<QualType, 4> Exceptions; EPI.ExceptionSpecType = FTI.getExceptionSpecType(); if (FTI.getExceptionSpecType() == EST_Dynamic) { Exceptions.reserve(FTI.NumExceptions); for (unsigned ei = 0, ee = FTI.NumExceptions; ei != ee; ++ei) { // FIXME: Preserve type source info. - QualType ET = GetTypeFromParser(FTI.Exceptions[ei].Ty); + QualType ET = S.GetTypeFromParser(FTI.Exceptions[ei].Ty); // Check that the type is valid for an exception spec, and // drop it if not. - if (!CheckSpecifiedExceptionType(ET, FTI.Exceptions[ei].Range)) + if (!S.CheckSpecifiedExceptionType(ET, FTI.Exceptions[ei].Range)) Exceptions.push_back(ET); } EPI.NumExceptions = Exceptions.size(); @@ -1973,7 +2218,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, if (!NoexceptExpr->isValueDependent() && !NoexceptExpr->isIntegerConstantExpr(Dummy, Context, &ErrLoc, /*evaluated*/false)) - Diag(ErrLoc, diag::err_noexcept_needs_constant_expression) + S.Diag(ErrLoc, diag::err_noexcept_needs_constant_expression) << NoexceptExpr->getSourceRange(); else EPI.NoexceptExpr = NoexceptExpr; @@ -1996,8 +2241,8 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, if (SS.isInvalid()) { // Avoid emitting extra errors if we already errored on the scope. D.setInvalidType(true); - } else if (isDependentScopeSpecifier(SS) || - dyn_cast_or_null<CXXRecordDecl>(computeDeclContext(SS))) { + } else if (S.isDependentScopeSpecifier(SS) || + dyn_cast_or_null<CXXRecordDecl>(S.computeDeclContext(SS))) { NestedNameSpecifier *NNS = static_cast<NestedNameSpecifier*>(SS.getScopeRep()); NestedNameSpecifier *NNSPrefix = NNS->getPrefix(); @@ -2026,7 +2271,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, break; } } else { - Diag(DeclType.Mem.Scope().getBeginLoc(), + S.Diag(DeclType.Mem.Scope().getBeginLoc(), diag::err_illegal_decl_mempointer_in_nonclass) << (D.getIdentifier() ? D.getIdentifier()->getName() : "type name") << DeclType.Mem.Scope().getRange(); @@ -2034,12 +2279,12 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } if (!ClsType.isNull()) - T = BuildMemberPointerType(T, ClsType, DeclType.Loc, D.getIdentifier()); + T = S.BuildMemberPointerType(T, ClsType, DeclType.Loc, D.getIdentifier()); if (T.isNull()) { T = Context.IntTy; D.setInvalidType(true); } else if (DeclType.Mem.TypeQuals) { - T = BuildQualifiedType(T, DeclType.Loc, DeclType.Mem.TypeQuals); + T = S.BuildQualifiedType(T, DeclType.Loc, DeclType.Mem.TypeQuals); } break; } @@ -2054,7 +2299,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, processTypeAttrs(state, T, false, attrs); } - if (getLangOptions().CPlusPlus && T->isFunctionType()) { + if (LangOpts.CPlusPlus && T->isFunctionType()) { const FunctionProtoType *FnTy = T->getAs<FunctionProtoType>(); assert(FnTy && "Why oh why is there not a FunctionProtoType here?"); @@ -2071,7 +2316,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, FreeFunction = (D.getContext() != Declarator::MemberContext || D.getDeclSpec().isFriendSpecified()); } else { - DeclContext *DC = computeDeclContext(D.getCXXScopeSpec()); + DeclContext *DC = S.computeDeclContext(D.getCXXScopeSpec()); FreeFunction = (DC && !DC->isRecord()); } @@ -2109,16 +2354,16 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, break; } - Diag(D.getIdentifierLoc(), + S.Diag(D.getIdentifierLoc(), diag::ext_qualified_function_type_template_arg) << Quals; } else { if (FnTy->getTypeQuals() != 0) { if (D.isFunctionDeclarator()) - Diag(D.getIdentifierLoc(), + S.Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_function_type); else - Diag(D.getIdentifierLoc(), + S.Diag(D.getIdentifierLoc(), diag::err_invalid_qualified_typedef_function_type_use) << FreeFunction; } @@ -2135,11 +2380,11 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, } } - Diag(Loc, diag::err_invalid_ref_qualifier_function_type) + S.Diag(Loc, diag::err_invalid_ref_qualifier_function_type) << (FnTy->getRefQualifier() == RQ_LValue) << FixItHint::CreateRemoval(Loc); } else { - Diag(D.getIdentifierLoc(), + S.Diag(D.getIdentifierLoc(), diag::err_invalid_ref_qualifier_typedef_function_type_use) << FreeFunction << (FnTy->getRefQualifier() == RQ_LValue); @@ -2192,7 +2437,7 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // We represent function parameter packs as function parameters whose // type is a pack expansion. if (!T->containsUnexpandedParameterPack()) { - Diag(D.getEllipsisLoc(), + S.Diag(D.getEllipsisLoc(), diag::err_function_parameter_pack_without_parameter_packs) << T << D.getSourceRange(); D.setEllipsisLoc(SourceLocation()); @@ -2212,14 +2457,15 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, // it expands those parameter packs. if (T->containsUnexpandedParameterPack()) T = Context.getPackExpansionType(T, llvm::Optional<unsigned>()); - else if (!getLangOptions().CPlusPlus0x) - Diag(D.getEllipsisLoc(), diag::ext_variadic_templates); + else if (!LangOpts.CPlusPlus0x) + S.Diag(D.getEllipsisLoc(), diag::ext_variadic_templates); break; case Declarator::FileContext: case Declarator::KNRTypeListContext: case Declarator::ObjCPrototypeContext: // FIXME: special diagnostic here? case Declarator::TypeNameContext: + case Declarator::CXXNewContext: case Declarator::AliasDeclContext: case Declarator::AliasTemplateContext: case Declarator::MemberContext: @@ -2227,11 +2473,12 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, case Declarator::ForContext: case Declarator::ConditionContext: case Declarator::CXXCatchContext: + case Declarator::ObjCCatchContext: case Declarator::BlockLiteralContext: case Declarator::TemplateTypeArgContext: // FIXME: We may want to allow parameter packs in block-literal contexts // in the future. - Diag(D.getEllipsisLoc(), diag::err_ellipsis_in_declarator_not_parameter); + S.Diag(D.getEllipsisLoc(), diag::err_ellipsis_in_declarator_not_parameter); D.setEllipsisLoc(SourceLocation()); break; } @@ -2241,7 +2488,138 @@ TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S, return Context.getNullTypeSourceInfo(); else if (D.isInvalidType()) return Context.getTrivialTypeSourceInfo(T); - return GetTypeSourceInfoForDeclarator(D, T, ReturnTypeInfo); + + return S.GetTypeSourceInfoForDeclarator(D, T, TInfo); +} + +/// GetTypeForDeclarator - Convert the type for the specified +/// declarator to Type instances. +/// +/// The result of this call will never be null, but the associated +/// type may be a null type if there's an unrecoverable error. +TypeSourceInfo *Sema::GetTypeForDeclarator(Declarator &D, Scope *S) { + // Determine the type of the declarator. Not all forms of declarator + // have a type. + + TypeProcessingState state(*this, D); + + TypeSourceInfo *ReturnTypeInfo = 0; + QualType T = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo); + if (T.isNull()) + return Context.getNullTypeSourceInfo(); + + if (D.isPrototypeContext() && getLangOptions().ObjCAutoRefCount) + inferARCWriteback(state, T); + + return GetFullTypeForDeclarator(state, T, ReturnTypeInfo); +} + +static void transferARCOwnershipToDeclSpec(Sema &S, + QualType &declSpecTy, + Qualifiers::ObjCLifetime ownership) { + if (declSpecTy->isObjCRetainableType() && + declSpecTy.getObjCLifetime() == Qualifiers::OCL_None) { + Qualifiers qs; + qs.addObjCLifetime(ownership); + declSpecTy = S.Context.getQualifiedType(declSpecTy, qs); + } +} + +static void transferARCOwnershipToDeclaratorChunk(TypeProcessingState &state, + Qualifiers::ObjCLifetime ownership, + unsigned chunkIndex) { + Sema &S = state.getSema(); + Declarator &D = state.getDeclarator(); + + // Look for an explicit lifetime attribute. + DeclaratorChunk &chunk = D.getTypeObject(chunkIndex); + for (const AttributeList *attr = chunk.getAttrs(); attr; + attr = attr->getNext()) + if (attr->getKind() == AttributeList::AT_objc_ownership) + return; + + const char *attrStr = 0; + switch (ownership) { + case Qualifiers::OCL_None: llvm_unreachable("no ownership!"); break; + case Qualifiers::OCL_ExplicitNone: attrStr = "none"; break; + case Qualifiers::OCL_Strong: attrStr = "strong"; break; + case Qualifiers::OCL_Weak: attrStr = "weak"; break; + case Qualifiers::OCL_Autoreleasing: attrStr = "autoreleasing"; break; + } + + // If there wasn't one, add one (with an invalid source location + // so that we don't make an AttributedType for it). + AttributeList *attr = D.getAttributePool() + .create(&S.Context.Idents.get("objc_ownership"), SourceLocation(), + /*scope*/ 0, SourceLocation(), + &S.Context.Idents.get(attrStr), SourceLocation(), + /*args*/ 0, 0, + /*declspec*/ false, /*C++0x*/ false); + spliceAttrIntoList(*attr, chunk.getAttrListRef()); + + // TODO: mark whether we did this inference? +} + +static void transferARCOwnership(TypeProcessingState &state, + QualType &declSpecTy, + Qualifiers::ObjCLifetime ownership) { + Sema &S = state.getSema(); + Declarator &D = state.getDeclarator(); + + int inner = -1; + for (unsigned i = 0, e = D.getNumTypeObjects(); i != e; ++i) { + DeclaratorChunk &chunk = D.getTypeObject(i); + switch (chunk.Kind) { + case DeclaratorChunk::Paren: + // Ignore parens. + break; + + case DeclaratorChunk::Array: + case DeclaratorChunk::Reference: + case DeclaratorChunk::Pointer: + inner = i; + break; + + case DeclaratorChunk::BlockPointer: + return transferARCOwnershipToDeclaratorChunk(state, ownership, i); + + case DeclaratorChunk::Function: + case DeclaratorChunk::MemberPointer: + return; + } + } + + if (inner == -1) + return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership); + + DeclaratorChunk &chunk = D.getTypeObject(inner); + if (chunk.Kind == DeclaratorChunk::Pointer) { + if (declSpecTy->isObjCRetainableType()) + return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership); + if (declSpecTy->isObjCObjectType()) + return transferARCOwnershipToDeclaratorChunk(state, ownership, inner); + } else { + assert(chunk.Kind == DeclaratorChunk::Array || + chunk.Kind == DeclaratorChunk::Reference); + return transferARCOwnershipToDeclSpec(S, declSpecTy, ownership); + } +} + +TypeSourceInfo *Sema::GetTypeForDeclaratorCast(Declarator &D, QualType FromTy) { + TypeProcessingState state(*this, D); + + TypeSourceInfo *ReturnTypeInfo = 0; + QualType declSpecTy = GetDeclSpecTypeForDeclarator(state, ReturnTypeInfo); + if (declSpecTy.isNull()) + return Context.getNullTypeSourceInfo(); + + if (getLangOptions().ObjCAutoRefCount) { + Qualifiers::ObjCLifetime ownership = Context.getInnerObjCOwnership(FromTy); + if (ownership != Qualifiers::OCL_None) + transferARCOwnership(state, declSpecTy, ownership); + } + + return GetFullTypeForDeclarator(state, declSpecTy, ReturnTypeInfo); } /// Map an AttributedType::Kind to an AttributeList::Kind. @@ -2259,6 +2637,8 @@ static AttributeList::Kind getAttrListKind(AttributedType::Kind kind) { return AttributeList::AT_neon_polyvector_type; case AttributedType::attr_objc_gc: return AttributeList::AT_objc_gc; + case AttributedType::attr_objc_ownership: + return AttributeList::AT_objc_ownership; case AttributedType::attr_noreturn: return AttributeList::AT_noreturn; case AttributedType::attr_cdecl: @@ -2489,6 +2869,9 @@ namespace { llvm_unreachable("qualified type locs not expected here!"); } + void VisitAttributedTypeLoc(AttributedTypeLoc TL) { + fillAttributedTypeLoc(TL, Chunk.getAttrs()); + } void VisitBlockPointerTypeLoc(BlockPointerTypeLoc TL) { assert(Chunk.Kind == DeclaratorChunk::BlockPointer); TL.setCaretLoc(Chunk.Loc); @@ -2657,8 +3040,7 @@ TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D) { // the parser. assert(D.getIdentifier() == 0 && "Type name should have no identifier!"); - TagDecl *OwnedTag = 0; - TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S, &OwnedTag); + TypeSourceInfo *TInfo = GetTypeForDeclarator(D, S); QualType T = TInfo->getType(); if (D.isInvalidType()) return true; @@ -2666,26 +3048,11 @@ TypeResult Sema::ActOnTypeName(Scope *S, Declarator &D) { if (getLangOptions().CPlusPlus) { // Check that there are no default arguments (C++ only). CheckExtraCXXDefaultArguments(D); - - // C++0x [dcl.type]p3: - // A type-specifier-seq shall not define a class or enumeration unless - // it appears in the type-id of an alias-declaration (7.1.3) that is not - // the declaration of a template-declaration. - if (OwnedTag && OwnedTag->isDefinition()) { - if (D.getContext() == Declarator::AliasTemplateContext) - Diag(OwnedTag->getLocation(), diag::err_type_defined_in_alias_template) - << Context.getTypeDeclType(OwnedTag); - else if (D.getContext() != Declarator::AliasDeclContext) - Diag(OwnedTag->getLocation(), diag::err_type_defined_in_type_specifier) - << Context.getTypeDeclType(OwnedTag); - } } return CreateParsedType(T, TInfo); } - - //===----------------------------------------------------------------------===// // Type Attribute Processing //===----------------------------------------------------------------------===// @@ -2744,6 +3111,99 @@ static void HandleAddressSpaceTypeAttribute(QualType &Type, Type = S.Context.getAddrSpaceQualType(Type, ASIdx); } +/// handleObjCOwnershipTypeAttr - Process an objc_ownership +/// attribute on the specified type. +/// +/// Returns 'true' if the attribute was handled. +static bool handleObjCOwnershipTypeAttr(TypeProcessingState &state, + AttributeList &attr, + QualType &type) { + if (!type->isObjCRetainableType() && !type->isDependentType()) + return false; + + Sema &S = state.getSema(); + + if (type.getQualifiers().getObjCLifetime()) { + S.Diag(attr.getLoc(), diag::err_attr_objc_ownership_redundant) + << type; + return true; + } + + if (!attr.getParameterName()) { + S.Diag(attr.getLoc(), diag::err_attribute_argument_n_not_string) + << "objc_ownership" << 1; + attr.setInvalid(); + return true; + } + + Qualifiers::ObjCLifetime lifetime; + if (attr.getParameterName()->isStr("none")) + lifetime = Qualifiers::OCL_ExplicitNone; + else if (attr.getParameterName()->isStr("strong")) + lifetime = Qualifiers::OCL_Strong; + else if (attr.getParameterName()->isStr("weak")) + lifetime = Qualifiers::OCL_Weak; + else if (attr.getParameterName()->isStr("autoreleasing")) + lifetime = Qualifiers::OCL_Autoreleasing; + else { + S.Diag(attr.getLoc(), diag::warn_attribute_type_not_supported) + << "objc_ownership" << attr.getParameterName(); + attr.setInvalid(); + return true; + } + + // Consume lifetime attributes without further comment outside of + // ARC mode. + if (!S.getLangOptions().ObjCAutoRefCount) + return true; + + Qualifiers qs; + qs.setObjCLifetime(lifetime); + QualType origType = type; + type = S.Context.getQualifiedType(type, qs); + + // If we have a valid source location for the attribute, use an + // AttributedType instead. + if (attr.getLoc().isValid()) + type = S.Context.getAttributedType(AttributedType::attr_objc_ownership, + origType, type); + + // Forbid __weak if the runtime doesn't support it. + if (lifetime == Qualifiers::OCL_Weak && + !S.getLangOptions().ObjCRuntimeHasWeak) { + + // Actually, delay this until we know what we're parsing. + if (S.DelayedDiagnostics.shouldDelayDiagnostics()) { + S.DelayedDiagnostics.add( + sema::DelayedDiagnostic::makeForbiddenType(attr.getLoc(), + diag::err_arc_weak_no_runtime, type, /*ignored*/ 0)); + } else { + S.Diag(attr.getLoc(), diag::err_arc_weak_no_runtime); + } + + attr.setInvalid(); + return true; + } + + // Forbid __weak for class objects marked as + // objc_arc_weak_reference_unavailable + if (lifetime == Qualifiers::OCL_Weak) { + QualType T = type; + while (const PointerType *ptr = T->getAs<PointerType>()) + T = ptr->getPointeeType(); + if (const ObjCObjectPointerType *ObjT = T->getAs<ObjCObjectPointerType>()) { + ObjCInterfaceDecl *Class = ObjT->getInterfaceDecl(); + if (Class->isArcWeakrefUnavailable()) { + S.Diag(attr.getLoc(), diag::err_arc_unsupported_weak_class); + S.Diag(ObjT->getInterfaceDecl()->getLocation(), + diag::note_class_declared); + } + } + } + + return true; +} + /// handleObjCGCTypeAttr - Process the __attribute__((objc_gc)) type /// attribute on the specified type. Returns true to indicate that /// the attribute was handled, false to indicate that the type does @@ -2954,6 +3414,23 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, return true; } + // ns_returns_retained is not always a type attribute, but if we got + // here, we're treating it as one right now. + if (attr.getKind() == AttributeList::AT_ns_returns_retained) { + assert(S.getLangOptions().ObjCAutoRefCount && + "ns_returns_retained treated as type attribute in non-ARC"); + if (attr.getNumArgs()) return true; + + // Delay if this is not a function type. + if (!unwrapped.isFunctionType()) + return false; + + FunctionType::ExtInfo EI + = unwrapped.get()->getExtInfo().withProducesResult(true); + type = unwrapped.wrap(S, S.Context.adjustFunctionType(unwrapped.get(), EI)); + return true; + } + if (attr.getKind() == AttributeList::AT_regparm) { unsigned value; if (S.CheckRegparmAttr(attr, value)) @@ -3127,6 +3604,40 @@ static void HandleVectorSizeAttr(QualType& CurType, const AttributeList &Attr, VectorType::GenericVector); } +/// \brief Process the OpenCL-like ext_vector_type attribute when it occurs on +/// a type. +static void HandleExtVectorTypeAttr(QualType &CurType, + const AttributeList &Attr, + Sema &S) { + Expr *sizeExpr; + + // Special case where the argument is a template id. + if (Attr.getParameterName()) { + CXXScopeSpec SS; + UnqualifiedId id; + id.setIdentifier(Attr.getParameterName(), Attr.getLoc()); + + ExprResult Size = S.ActOnIdExpression(S.getCurScope(), SS, id, false, + false); + if (Size.isInvalid()) + return; + + sizeExpr = Size.get(); + } else { + // check the attribute arguments. + if (Attr.getNumArgs() != 1) { + S.Diag(Attr.getLoc(), diag::err_attribute_wrong_number_arguments) << 1; + return; + } + sizeExpr = Attr.getArg(0); + } + + // Create the vector type. + QualType T = S.BuildExtVectorType(CurType, sizeExpr, Attr.getLoc()); + if (!T.isNull()) + CurType = T; +} + /// HandleNeonVectorTypeAttr - The "neon_vector_type" and /// "neon_polyvector_type" attributes are used to create vector types that /// are mangled according to ARM's ABI. Otherwise, these types are identical @@ -3217,6 +3728,11 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, case AttributeList::AT_vector_size: HandleVectorSizeAttr(type, attr, state.getSema()); break; + case AttributeList::AT_ext_vector_type: + if (state.getDeclarator().getDeclSpec().getStorageClassSpec() + != DeclSpec::SCS_typedef) + HandleExtVectorTypeAttr(type, attr, state.getSema()); + break; case AttributeList::AT_neon_vector_type: HandleNeonVectorTypeAttr(type, attr, state.getSema(), VectorType::NeonVector, "neon_vector_type"); @@ -3226,11 +3742,15 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, VectorType::NeonPolyVector, "neon_polyvector_type"); break; - case AttributeList::AT_opencl_image_access: HandleOpenCLImageAccessAttribute(type, attr, state.getSema()); break; + case AttributeList::AT_ns_returns_retained: + if (!state.getSema().getLangOptions().ObjCAutoRefCount) + break; + // fallthrough into the function attrs + FUNCTION_TYPE_ATTRS_CASELIST: // Never process function type attributes as part of the // declaration-specifiers. diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h index ff2e46a9026b..fa87217821aa 100644 --- a/lib/Sema/TreeTransform.h +++ b/lib/Sema/TreeTransform.h @@ -1192,6 +1192,16 @@ public: Body); } + /// \brief Build a new Objective-C @autoreleasepool statement. + /// + /// By default, performs semantic analysis to build the new statement. + /// Subclasses may override this routine to provide different behavior. + StmtResult RebuildObjCAutoreleasePoolStmt(SourceLocation AtLoc, + Stmt *Body) { + return getSema().ActOnObjCAutoreleasePoolStmt(AtLoc, Body); + } + + /// \brief Build a new Objective-C fast enumeration statement. /// /// By default, performs semantic analysis to build the new statement. @@ -1537,9 +1547,9 @@ public: /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. ExprResult RebuildInitList(SourceLocation LBraceLoc, - MultiExprArg Inits, - SourceLocation RBraceLoc, - QualType ResultTy) { + MultiExprArg Inits, + SourceLocation RBraceLoc, + QualType ResultTy) { ExprResult Result = SemaRef.ActOnInitList(LBraceLoc, move(Inits), RBraceLoc); if (Result.isInvalid() || ResultTy->isDependentType()) @@ -1856,8 +1866,9 @@ public: /// /// By default, performs semantic analysis to build the new expression. /// Subclasses may override this routine to provide different behavior. - ExprResult RebuildCXXThrowExpr(SourceLocation ThrowLoc, Expr *Sub) { - return getSema().ActOnCXXThrow(ThrowLoc, Sub); + ExprResult RebuildCXXThrowExpr(SourceLocation ThrowLoc, Expr *Sub, + bool IsThrownVariableInScope) { + return getSema().BuildCXXThrow(ThrowLoc, Sub, IsThrownVariableInScope); } /// \brief Build a new C++ default-argument expression. @@ -2466,6 +2477,10 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, Outputs.push_back(Out.get()); continue; } + + // Record right away that the argument was changed. This needs + // to happen even if the array expands to nothing. + if (ArgChanged) *ArgChanged = true; // The transform has determined that we should perform an elementwise // expansion of the pattern. Do so. @@ -2482,8 +2497,6 @@ bool TreeTransform<Derived>::TransformExprs(Expr **Inputs, return true; } - if (ArgChanged) - *ArgChanged = true; Outputs.push_back(Out.get()); } @@ -3162,6 +3175,38 @@ TreeTransform<Derived>::TransformQualifiedType(TypeLocBuilder &TLB, if (Result->isFunctionType() || Result->isReferenceType()) return Result; + // Suppress Objective-C lifetime qualifiers if they don't make sense for the + // resulting type. + if (Quals.hasObjCLifetime()) { + if (!Result->isObjCLifetimeType() && !Result->isDependentType()) + Quals.removeObjCLifetime(); + else if (Result.getObjCLifetime()) { + // Objective-C ARC: + // A lifetime qualifier applied to a substituted template parameter + // overrides the lifetime qualifier from the template argument. + if (const SubstTemplateTypeParmType *SubstTypeParam + = dyn_cast<SubstTemplateTypeParmType>(Result)) { + QualType Replacement = SubstTypeParam->getReplacementType(); + Qualifiers Qs = Replacement.getQualifiers(); + Qs.removeObjCLifetime(); + Replacement + = SemaRef.Context.getQualifiedType(Replacement.getUnqualifiedType(), + Qs); + Result = SemaRef.Context.getSubstTemplateTypeParmType( + SubstTypeParam->getReplacedParameter(), + Replacement); + TLB.TypeWasModifiedSafely(Result); + } else { + // Otherwise, complain about the addition of a qualifier to an + // already-qualified type. + SourceRange R = TLB.getTemporaryTypeLoc(Result).getSourceRange(); + SemaRef.Diag(R.getBegin(), diag::err_attr_objc_ownership_redundant) + << Result << R; + + Quals.removeObjCLifetime(); + } + } + } if (!Quals.empty()) { Result = SemaRef.BuildQualifiedType(Result, T.getBeginLoc(), Quals); TLB.push<QualifiedTypeLoc>(Result); @@ -3333,7 +3378,11 @@ QualType TreeTransform<Derived>::TransformPointerType(TypeLocBuilder &TLB, if (Result.isNull()) return QualType(); } - + + // Objective-C ARC can add lifetime qualifiers to the type that we're + // pointing to. + TLB.TypeWasModifiedSafely(Result->getPointeeType()); + PointerTypeLoc NewT = TLB.push<PointerTypeLoc>(Result); NewT.setSigilLoc(TL.getSigilLoc()); return Result; @@ -3387,6 +3436,11 @@ TreeTransform<Derived>::TransformReferenceType(TypeLocBuilder &TLB, return QualType(); } + // Objective-C ARC can add lifetime qualifiers to the type that we're + // referring to. + TLB.TypeWasModifiedSafely( + Result->getAs<ReferenceType>()->getPointeeTypeAsWritten()); + // r-value references can be rebuilt as l-value references. ReferenceTypeLoc NewTL; if (isa<LValueReferenceType>(Result)) @@ -5435,6 +5489,25 @@ TreeTransform<Derived>::TransformObjCAtSynchronizedStmt( template<typename Derived> StmtResult +TreeTransform<Derived>::TransformObjCAutoreleasePoolStmt( + ObjCAutoreleasePoolStmt *S) { + // Transform the body. + StmtResult Body = getDerived().TransformStmt(S->getSubStmt()); + if (Body.isInvalid()) + return StmtError(); + + // If nothing changed, just retain this statement. + if (!getDerived().AlwaysRebuild() && + Body.get() == S->getSubStmt()) + return SemaRef.Owned(S); + + // Build a new statement. + return getDerived().RebuildObjCAutoreleasePoolStmt( + S->getAtLoc(), Body.get()); +} + +template<typename Derived> +StmtResult TreeTransform<Derived>::TransformObjCForCollectionStmt( ObjCForCollectionStmt *S) { // Transform the element statement. @@ -6721,7 +6794,8 @@ TreeTransform<Derived>::TransformCXXThrowExpr(CXXThrowExpr *E) { SubExpr.get() == E->getSubExpr()) return SemaRef.Owned(E); - return getDerived().RebuildCXXThrowExpr(E->getThrowLoc(), SubExpr.get()); + return getDerived().RebuildCXXThrowExpr(E->getThrowLoc(), SubExpr.get(), + E->isThrownVariableInScope()); } template<typename Derived> @@ -6867,9 +6941,13 @@ TreeTransform<Derived>::TransformCXXNewExpr(CXXNewExpr *E) { AllocType, AllocTypeInfo, ArraySize.get(), - /*FIXME:*/E->getLocStart(), + /*FIXME:*/E->hasInitializer() + ? E->getLocStart() + : SourceLocation(), move_arg(ConstructorArgs), - E->getLocEnd()); + /*FIXME:*/E->hasInitializer() + ? E->getLocEnd() + : SourceLocation()); } template<typename Derived> @@ -7587,6 +7665,21 @@ TreeTransform<Derived>::TransformSubstNonTypeTemplateParmPackExpr( template<typename Derived> ExprResult +TreeTransform<Derived>::TransformSubstNonTypeTemplateParmExpr( + SubstNonTypeTemplateParmExpr *E) { + // Default behavior is to do nothing with this transformation. + return SemaRef.Owned(E); +} + +template<typename Derived> +ExprResult +TreeTransform<Derived>::TransformMaterializeTemporaryExpr( + MaterializeTemporaryExpr *E) { + return getDerived().TransformExpr(E->GetTemporaryExpr()); +} + +template<typename Derived> +ExprResult TreeTransform<Derived>::TransformObjCStringLiteral(ObjCStringLiteral *E) { return SemaRef.Owned(E); } @@ -7609,6 +7702,43 @@ TreeTransform<Derived>::TransformObjCEncodeExpr(ObjCEncodeExpr *E) { } template<typename Derived> +ExprResult TreeTransform<Derived>:: +TransformObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + ExprResult result = getDerived().TransformExpr(E->getSubExpr()); + if (result.isInvalid()) return ExprError(); + Expr *subExpr = result.take(); + + if (!getDerived().AlwaysRebuild() && + subExpr == E->getSubExpr()) + return SemaRef.Owned(E); + + return SemaRef.Owned(new(SemaRef.Context) + ObjCIndirectCopyRestoreExpr(subExpr, E->getType(), E->shouldCopy())); +} + +template<typename Derived> +ExprResult TreeTransform<Derived>:: +TransformObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + TypeSourceInfo *TSInfo + = getDerived().TransformType(E->getTypeInfoAsWritten()); + if (!TSInfo) + return ExprError(); + + ExprResult Result = getDerived().TransformExpr(E->getSubExpr()); + if (Result.isInvalid()) + return ExprError(); + + if (!getDerived().AlwaysRebuild() && + TSInfo == E->getTypeInfoAsWritten() && + Result.get() == E->getSubExpr()) + return SemaRef.Owned(E); + + return SemaRef.BuildObjCBridgedCast(E->getLParenLoc(), E->getBridgeKind(), + E->getBridgeKeywordLoc(), TSInfo, + Result.get()); +} + +template<typename Derived> ExprResult TreeTransform<Derived>::TransformObjCMessageExpr(ObjCMessageExpr *E) { // Transform arguments. @@ -8227,11 +8357,24 @@ TreeTransform<Derived>::RebuildCXXOperatorCallExpr(OverloadedOperatorKind Op, return SemaRef.CreateOverloadedUnaryOp(OpLoc, Opc, Functions, First); } - if (Op == OO_Subscript) - return SemaRef.CreateOverloadedArraySubscriptExpr(Callee->getLocStart(), - OpLoc, - First, - Second); + if (Op == OO_Subscript) { + SourceLocation LBrace; + SourceLocation RBrace; + + if (DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Callee)) { + DeclarationNameLoc &NameLoc = DRE->getNameInfo().getInfo(); + LBrace = SourceLocation::getFromRawEncoding( + NameLoc.CXXOperatorName.BeginOpNameLoc); + RBrace = SourceLocation::getFromRawEncoding( + NameLoc.CXXOperatorName.EndOpNameLoc); + } else { + LBrace = Callee->getLocStart(); + RBrace = OpLoc; + } + + return SemaRef.CreateOverloadedArraySubscriptExpr(LBrace, RBrace, + First, Second); + } // Create the overloaded operator invocation for binary operators. BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op); diff --git a/lib/Sema/TypeLocBuilder.h b/lib/Sema/TypeLocBuilder.h index 3570737d11bc..7a5e43e25dc9 100644 --- a/lib/Sema/TypeLocBuilder.h +++ b/lib/Sema/TypeLocBuilder.h @@ -16,7 +16,6 @@ #define LLVM_CLANG_SEMA_TYPELOCBUILDER_H #include "clang/AST/TypeLoc.h" -#include "llvm/ADT/SmallVector.h" #include "clang/AST/ASTContext.h" namespace clang { @@ -87,6 +86,14 @@ class TypeLocBuilder { Index = Capacity; } + /// \brief Tell the TypeLocBuilder that the type it is storing has been + /// modified in some safe way that doesn't affect type-location information. + void TypeWasModifiedSafely(QualType T) { +#ifndef NDEBUG + LastTy = T; +#endif + } + /// Pushes space for a new TypeLoc of the given type. Invalidates /// any TypeLocs previously retrieved from this builder. template <class TyLocType> TyLocType push(QualType T) { @@ -139,7 +146,7 @@ private: Index -= LocalSize; - return getTypeLoc(T); + return getTemporaryTypeLoc(T); } /// Grow to the given capacity. @@ -171,15 +178,17 @@ private: reserve(Size); Index -= Size; - return getTypeLoc(T); + return getTemporaryTypeLoc(T); } - - // This is private because, when we kill off TypeSourceInfo in favor - // of TypeLoc, we'll want an interface that creates a TypeLoc given - // an ASTContext, and we don't want people to think they can just - // use this as an equivalent. - TypeLoc getTypeLoc(QualType T) { +public: + /// \brief Retrieve a temporary TypeLoc that refers into this \c TypeLocBuilder + /// object. + /// + /// The resulting \c TypeLoc should only be used so long as the + /// \c TypeLocBuilder is active and has not had more type information + /// pushed into it. + TypeLoc getTemporaryTypeLoc(QualType T) { #ifndef NDEBUG assert(LastTy == T && "type doesn't match last type pushed!"); #endif diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index 8fb20d22b7de..a4ed5f4da48c 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -147,9 +147,11 @@ PCHValidator::ReadLanguageOptions(const LangOptions &LangOpts) { PARSE_LANGOPT_IMPORTANT(OpenCL, diag::warn_pch_opencl); PARSE_LANGOPT_IMPORTANT(CUDA, diag::warn_pch_cuda); PARSE_LANGOPT_BENIGN(CatchUndefined); + PARSE_LANGOPT_BENIGN(DefaultFPContract); PARSE_LANGOPT_IMPORTANT(ElideConstructors, diag::warn_pch_elide_constructors); PARSE_LANGOPT_BENIGN(SpellChecking); - PARSE_LANGOPT_BENIGN(DefaultFPContract); + PARSE_LANGOPT_IMPORTANT(ObjCAutoRefCount, diag::warn_pch_auto_ref_count); + PARSE_LANGOPT_BENIGN(ObjCInferRelatedReturnType); #undef PARSE_LANGOPT_IMPORTANT #undef PARSE_LANGOPT_BENIGN @@ -655,7 +657,8 @@ public: // and associate it with the persistent ID. IdentifierInfo *II = KnownII; if (!II) - II = &Reader.getIdentifierTable().getOwn(k.first, k.first + k.second); + II = &Reader.getIdentifierTable().getOwn(llvm::StringRef(k.first, + k.second)); Reader.SetIdentifierInfo(ID, II); II->setIsFromAST(); return II; @@ -682,7 +685,8 @@ public: // the new IdentifierInfo. IdentifierInfo *II = KnownII; if (!II) - II = &Reader.getIdentifierTable().getOwn(k.first, k.first + k.second); + II = &Reader.getIdentifierTable().getOwn(llvm::StringRef(k.first, + k.second)); Reader.SetIdentifierInfo(ID, II); // Set or check the various bits in the IdentifierInfo structure. @@ -999,8 +1003,7 @@ bool ASTReader::ParseLineTable(PerFileData &F, std::string Filename(&Record[Idx], &Record[Idx] + FilenameLen); Idx += FilenameLen; MaybeAddSystemRootToFilename(Filename); - FileIDs[I] = LineTable.getLineTableFilenameID(Filename.c_str(), - Filename.size()); + FileIDs[I] = LineTable.getLineTableFilenameID(Filename); } // Parse the line entries @@ -1192,7 +1195,7 @@ ASTReader::ASTReadResult ASTReader::ReadSourceManagerBlock(PerFileData &F) { case SM_SLOC_FILE_ENTRY: case SM_SLOC_BUFFER_ENTRY: - case SM_SLOC_INSTANTIATION_ENTRY: + case SM_SLOC_EXPANSION_ENTRY: // Once we hit one of the source location entries, we're done. return Success; } @@ -1362,7 +1365,7 @@ ASTReader::ASTReadResult ASTReader::ReadSLocEntryRecord(unsigned ID) { break; } - case SM_SLOC_INSTANTIATION_ENTRY: { + case SM_SLOC_EXPANSION_ENTRY: { SourceLocation SpellingLoc = ReadSourceLocation(*F, Record[1]); SourceMgr.createInstantiationLoc(SpellingLoc, ReadSourceLocation(*F, Record[2]), @@ -1553,17 +1556,17 @@ PreprocessedEntity *ASTReader::LoadPreprocessedEntity(PerFileData &F) { (PreprocessorDetailRecordTypes)F.PreprocessorDetailCursor.ReadRecord( Code, Record, BlobStart, BlobLen); switch (RecType) { - case PPD_MACRO_INSTANTIATION: { + case PPD_MACRO_EXPANSION: { if (PreprocessedEntity *PE = PPRec.getPreprocessedEntity(Record[0])) return PE; - MacroInstantiation *MI - = new (PPRec) MacroInstantiation(DecodeIdentifierInfo(Record[3]), + MacroExpansion *ME = + new (PPRec) MacroExpansion(DecodeIdentifierInfo(Record[3]), SourceRange(ReadSourceLocation(F, Record[1]), ReadSourceLocation(F, Record[2])), - getMacroDefinition(Record[4])); - PPRec.SetPreallocatedEntity(Record[0], MI); - return MI; + getMacroDefinition(Record[4])); + PPRec.SetPreallocatedEntity(Record[0], ME); + return ME; } case PPD_MACRO_DEFINITION: { @@ -2366,6 +2369,15 @@ ASTReader::ReadASTBlock(PerFileData &F) { TentativeDefinitions.insert(TentativeDefinitions.end(), Record.begin(), Record.end()); break; + + case KNOWN_NAMESPACES: + // Optimization for the first block. + if (KnownNamespaces.empty()) + KnownNamespaces.swap(Record); + else + KnownNamespaces.insert(KnownNamespaces.end(), + Record.begin(), Record.end()); + break; } First = false; } @@ -2980,6 +2992,7 @@ bool ASTReader::ParseLanguageOptions( PARSE_LANGOPT(ElideConstructors); PARSE_LANGOPT(SpellChecking); PARSE_LANGOPT(MRTD); + PARSE_LANGOPT(ObjCAutoRefCount); #undef PARSE_LANGOPT return Listener->ReadLanguageOptions(LangOpts); @@ -3225,12 +3238,13 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { } case TYPE_FUNCTION_NO_PROTO: { - if (Record.size() != 5) { + if (Record.size() != 6) { Error("incorrect encoding of no-proto function type"); return QualType(); } QualType ResultType = GetType(Record[0]); - FunctionType::ExtInfo Info(Record[1], Record[2], Record[3], (CallingConv)Record[4]); + FunctionType::ExtInfo Info(Record[1], Record[2], Record[3], + (CallingConv)Record[4], Record[5]); return Context->getFunctionNoProtoType(ResultType, Info); } @@ -3241,9 +3255,10 @@ QualType ASTReader::ReadTypeRecord(unsigned Index) { EPI.ExtInfo = FunctionType::ExtInfo(/*noreturn*/ Record[1], /*hasregparm*/ Record[2], /*regparm*/ Record[3], - static_cast<CallingConv>(Record[4])); + static_cast<CallingConv>(Record[4]), + /*produces*/ Record[5]); - unsigned Idx = 5; + unsigned Idx = 6; unsigned NumParams = Record[Idx++]; llvm::SmallVector<QualType, 16> ParamTypes; for (unsigned I = 0; I != NumParams; ++I) @@ -3993,12 +4008,9 @@ Stmt *ASTReader::GetExternalDeclStmt(uint64_t Offset) { llvm_unreachable("Broken chain"); } -bool ASTReader::FindExternalLexicalDecls(const DeclContext *DC, +ExternalLoadResult ASTReader::FindExternalLexicalDecls(const DeclContext *DC, bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl<Decl*> &Decls) { - assert(DC->hasExternalLexicalStorage() && - "DeclContext has no lexical decls in storage"); - // There might be lexical decls in multiple parts of the chain, for the TU // at least. // DeclContextOffsets might reallocate as we load additional decls below, @@ -4023,7 +4035,7 @@ bool ASTReader::FindExternalLexicalDecls(const DeclContext *DC, } ++NumLexicalDeclContextsRead; - return false; + return ELR_Success; } DeclContext::lookup_result @@ -4446,6 +4458,17 @@ ASTReader::ReadMethodPool(Selector Sel) { return std::pair<ObjCMethodList, ObjCMethodList>(); } +void ASTReader::ReadKnownNamespaces( + llvm::SmallVectorImpl<NamespaceDecl *> &Namespaces) { + Namespaces.clear(); + + for (unsigned I = 0, N = KnownNamespaces.size(); I != N; ++I) { + if (NamespaceDecl *Namespace + = dyn_cast_or_null<NamespaceDecl>(GetDecl(KnownNamespaces[I]))) + Namespaces.push_back(Namespace); + } +} + void ASTReader::LoadSelector(Selector Sel) { // It would be complicated to avoid reading the methods anyway. So don't. ReadMethodPool(Sel); @@ -4540,7 +4563,7 @@ IdentifierInfo *ASTReader::DecodeIdentifierInfo(unsigned ID) { unsigned StrLen = (((unsigned) StrLenPtr[0]) | (((unsigned) StrLenPtr[1]) << 8)) - 1; IdentifiersLoaded[ID] - = &PP->getIdentifierTable().get(Str, StrLen); + = &PP->getIdentifierTable().get(llvm::StringRef(Str, StrLen)); if (DeserializationListener) DeserializationListener->IdentifierRead(ID + 1, IdentifiersLoaded[ID]); } @@ -4716,6 +4739,14 @@ ASTReader::ReadTemplateName(PerFileData &F, const RecordData &Record, return Context->getDependentTemplateName(NNS, (OverloadedOperatorKind)Record[Idx++]); } + + case TemplateName::SubstTemplateTemplateParm: { + TemplateTemplateParmDecl *param + = cast_or_null<TemplateTemplateParmDecl>(GetDecl(Record[Idx++])); + if (!param) return TemplateName(); + TemplateName replacement = ReadTemplateName(F, Record, Idx); + return Context->getSubstTemplateTemplateParm(param, replacement); + } case TemplateName::SubstTemplateTemplateParmPack: { TemplateTemplateParmDecl *Param diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index fab2069bc5f4..24ab544dcd1c 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -710,6 +710,7 @@ void ASTDeclReader::VisitVarDecl(VarDecl *VD) { VD->VarDeclBits.ExceptionVar = Record[Idx++]; VD->VarDeclBits.NRVOVariable = Record[Idx++]; VD->VarDeclBits.CXXForRangeDecl = Record[Idx++]; + VD->VarDeclBits.ARCPseudoStrong = Record[Idx++]; if (Record[Idx++]) VD->setInit(Reader.ReadExpr(F)); diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp index f3f67a76c4d4..14927b9e3bc8 100644 --- a/lib/Serialization/ASTReaderStmt.cpp +++ b/lib/Serialization/ASTReaderStmt.cpp @@ -60,136 +60,16 @@ namespace clang { /// \brief The number of record fields required for the Expr class /// itself. - static const unsigned NumExprFields = NumStmtFields + 6; + static const unsigned NumExprFields = NumStmtFields + 7; /// \brief Read and initialize a ExplicitTemplateArgumentList structure. void ReadExplicitTemplateArgumentList(ExplicitTemplateArgumentList &ArgList, unsigned NumTemplateArgs); void VisitStmt(Stmt *S); - void VisitNullStmt(NullStmt *S); - void VisitCompoundStmt(CompoundStmt *S); - void VisitSwitchCase(SwitchCase *S); - void VisitCaseStmt(CaseStmt *S); - void VisitDefaultStmt(DefaultStmt *S); - void VisitLabelStmt(LabelStmt *S); - void VisitIfStmt(IfStmt *S); - void VisitSwitchStmt(SwitchStmt *S); - void VisitWhileStmt(WhileStmt *S); - void VisitDoStmt(DoStmt *S); - void VisitForStmt(ForStmt *S); - void VisitGotoStmt(GotoStmt *S); - void VisitIndirectGotoStmt(IndirectGotoStmt *S); - void VisitContinueStmt(ContinueStmt *S); - void VisitBreakStmt(BreakStmt *S); - void VisitReturnStmt(ReturnStmt *S); - void VisitDeclStmt(DeclStmt *S); - void VisitAsmStmt(AsmStmt *S); - void VisitExpr(Expr *E); - void VisitPredefinedExpr(PredefinedExpr *E); - void VisitDeclRefExpr(DeclRefExpr *E); - void VisitIntegerLiteral(IntegerLiteral *E); - void VisitFloatingLiteral(FloatingLiteral *E); - void VisitImaginaryLiteral(ImaginaryLiteral *E); - void VisitStringLiteral(StringLiteral *E); - void VisitCharacterLiteral(CharacterLiteral *E); - void VisitParenExpr(ParenExpr *E); - void VisitParenListExpr(ParenListExpr *E); - void VisitUnaryOperator(UnaryOperator *E); - void VisitOffsetOfExpr(OffsetOfExpr *E); - void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); - void VisitArraySubscriptExpr(ArraySubscriptExpr *E); - void VisitCallExpr(CallExpr *E); - void VisitMemberExpr(MemberExpr *E); - void VisitCastExpr(CastExpr *E); - void VisitBinaryOperator(BinaryOperator *E); - void VisitCompoundAssignOperator(CompoundAssignOperator *E); - void VisitConditionalOperator(ConditionalOperator *E); - void VisitBinaryConditionalOperator(BinaryConditionalOperator *E); - void VisitImplicitCastExpr(ImplicitCastExpr *E); - void VisitExplicitCastExpr(ExplicitCastExpr *E); - void VisitCStyleCastExpr(CStyleCastExpr *E); - void VisitCompoundLiteralExpr(CompoundLiteralExpr *E); - void VisitExtVectorElementExpr(ExtVectorElementExpr *E); - void VisitInitListExpr(InitListExpr *E); - void VisitDesignatedInitExpr(DesignatedInitExpr *E); - void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); - void VisitVAArgExpr(VAArgExpr *E); - void VisitAddrLabelExpr(AddrLabelExpr *E); - void VisitStmtExpr(StmtExpr *E); - void VisitChooseExpr(ChooseExpr *E); - void VisitGNUNullExpr(GNUNullExpr *E); - void VisitShuffleVectorExpr(ShuffleVectorExpr *E); - void VisitBlockExpr(BlockExpr *E); - void VisitBlockDeclRefExpr(BlockDeclRefExpr *E); - void VisitGenericSelectionExpr(GenericSelectionExpr *E); - void VisitObjCStringLiteral(ObjCStringLiteral *E); - void VisitObjCEncodeExpr(ObjCEncodeExpr *E); - void VisitObjCSelectorExpr(ObjCSelectorExpr *E); - void VisitObjCProtocolExpr(ObjCProtocolExpr *E); - void VisitObjCIvarRefExpr(ObjCIvarRefExpr *E); - void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E); - void VisitObjCMessageExpr(ObjCMessageExpr *E); - void VisitObjCIsaExpr(ObjCIsaExpr *E); - - void VisitObjCForCollectionStmt(ObjCForCollectionStmt *); - void VisitObjCAtCatchStmt(ObjCAtCatchStmt *); - void VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *); - void VisitObjCAtTryStmt(ObjCAtTryStmt *); - void VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *); - void VisitObjCAtThrowStmt(ObjCAtThrowStmt *); - - // C++ Statements - void VisitCXXCatchStmt(CXXCatchStmt *S); - void VisitCXXTryStmt(CXXTryStmt *S); - void VisitCXXForRangeStmt(CXXForRangeStmt *); - - void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); - void VisitCXXConstructExpr(CXXConstructExpr *E); - void VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E); - void VisitCXXNamedCastExpr(CXXNamedCastExpr *E); - void VisitCXXStaticCastExpr(CXXStaticCastExpr *E); - void VisitCXXDynamicCastExpr(CXXDynamicCastExpr *E); - void VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr *E); - void VisitCXXConstCastExpr(CXXConstCastExpr *E); - void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E); - void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E); - void VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr *E); - void VisitCXXTypeidExpr(CXXTypeidExpr *E); - void VisitCXXUuidofExpr(CXXUuidofExpr *E); - void VisitCXXThisExpr(CXXThisExpr *E); - void VisitCXXThrowExpr(CXXThrowExpr *E); - void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E); - void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); - - void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E); - void VisitCXXNewExpr(CXXNewExpr *E); - void VisitCXXDeleteExpr(CXXDeleteExpr *E); - void VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E); - - void VisitExprWithCleanups(ExprWithCleanups *E); - - void VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E); - void VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E); - void VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E); - - void VisitOverloadExpr(OverloadExpr *E); - void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E); - void VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E); - - void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E); - void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E); - void VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *E); - void VisitExpressionTraitExpr(ExpressionTraitExpr *E); - void VisitCXXNoexceptExpr(CXXNoexceptExpr *E); - void VisitPackExpansionExpr(PackExpansionExpr *E); - void VisitSizeOfPackExpr(SizeOfPackExpr *E); - void VisitSubstNonTypeTemplateParmPackExpr( - SubstNonTypeTemplateParmPackExpr *E); - void VisitOpaqueValueExpr(OpaqueValueExpr *E); - - // CUDA Expressions - void VisitCUDAKernelCallExpr(CUDAKernelCallExpr *E); +#define STMT(Type, Base) \ + void Visit##Type(Type *); +#include "clang/AST/StmtNodes.inc" }; } @@ -410,6 +290,7 @@ void ASTStmtReader::VisitExpr(Expr *E) { E->setType(Reader.GetType(Record[Idx++])); E->setTypeDependent(Record[Idx++]); E->setValueDependent(Record[Idx++]); + E->setInstantiationDependent(Record[Idx++]); E->ExprBits.ContainsUnexpandedParameterPack = Record[Idx++]; E->setValueKind(static_cast<ExprValueKind>(Record[Idx++])); E->setObjectKind(static_cast<ExprObjectKind>(Record[Idx++])); @@ -589,6 +470,10 @@ void ASTStmtReader::VisitCallExpr(CallExpr *E) { E->setArg(I, Reader.ReadSubExpr()); } +void ASTStmtReader::VisitCXXMemberCallExpr(CXXMemberCallExpr *E) { + VisitCallExpr(E); +} + void ASTStmtReader::VisitMemberExpr(MemberExpr *E) { // Don't call VisitExpr, this is fully initialized at creation. assert(E->getStmtClass() == Stmt::MemberExprClass && @@ -602,6 +487,20 @@ void ASTStmtReader::VisitObjCIsaExpr(ObjCIsaExpr *E) { E->setArrow(Record[Idx++]); } +void ASTStmtReader:: +VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + VisitExpr(E); + E->Operand = Reader.ReadSubExpr(); + E->setShouldCopy(Record[Idx++]); +} + +void ASTStmtReader::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + VisitExplicitCastExpr(E); + E->LParenLoc = ReadSourceLocation(Record, Idx); + E->BridgeKeywordLoc = ReadSourceLocation(Record, Idx); + E->Kind = Record[Idx++]; +} + void ASTStmtReader::VisitCastExpr(CastExpr *E) { VisitExpr(E); unsigned NumBaseSpecs = Record[Idx++]; @@ -929,6 +828,7 @@ void ASTStmtReader::VisitObjCMessageExpr(ObjCMessageExpr *E) { VisitExpr(E); assert(Record[Idx] == E->getNumArgs()); ++Idx; + E->setDelegateInitCall(Record[Idx++]); ObjCMessageExpr::ReceiverKind Kind = static_cast<ObjCMessageExpr::ReceiverKind>(Record[Idx++]); switch (Kind) { @@ -987,6 +887,12 @@ void ASTStmtReader::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { S->setAtFinallyLoc(ReadSourceLocation(Record, Idx)); } +void ASTStmtReader::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + VisitStmt(S); + S->setSubStmt(Reader.ReadSubStmt()); + S->setAtLoc(ReadSourceLocation(Record, Idx)); +} + void ASTStmtReader::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { VisitStmt(S); assert(Record[Idx] == S->getNumCatchStmts()); @@ -1125,18 +1031,6 @@ void ASTStmtReader::VisitCXXTypeidExpr(CXXTypeidExpr *E) { // typeid(42+2) E->setExprOperand(Reader.ReadSubExpr()); } -void ASTStmtReader::VisitCXXUuidofExpr(CXXUuidofExpr *E) { - VisitExpr(E); - E->setSourceRange(ReadSourceRange(Record, Idx)); - if (E->isTypeOperand()) { // __uuidof(ComType) - E->setTypeOperandSourceInfo( - GetTypeSourceInfo(Record, Idx)); - return; - } - - // __uuidof(expr) - E->setExprOperand(Reader.ReadSubExpr()); -} void ASTStmtReader::VisitCXXThisExpr(CXXThisExpr *E) { VisitExpr(E); @@ -1146,8 +1040,9 @@ void ASTStmtReader::VisitCXXThisExpr(CXXThisExpr *E) { void ASTStmtReader::VisitCXXThrowExpr(CXXThrowExpr *E) { VisitExpr(E); - E->setThrowLoc(ReadSourceLocation(Record, Idx)); - E->setSubExpr(Reader.ReadSubExpr()); + E->ThrowLoc = ReadSourceLocation(Record, Idx); + E->Op = Reader.ReadSubExpr(); + E->IsThrownVariableInScope = Record[Idx++]; } void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) { @@ -1388,6 +1283,15 @@ void ASTStmtReader::VisitSizeOfPackExpr(SizeOfPackExpr *E) { E->Pack = cast_or_null<NamedDecl>(Reader.GetDecl(Record[Idx++])); } +void ASTStmtReader::VisitSubstNonTypeTemplateParmExpr( + SubstNonTypeTemplateParmExpr *E) { + VisitExpr(E); + E->Param + = cast_or_null<NonTypeTemplateParmDecl>(Reader.GetDecl(Record[Idx++])); + E->NameLoc = ReadSourceLocation(Record, Idx); + E->Replacement = Reader.ReadSubExpr(); +} + void ASTStmtReader::VisitSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *E) { VisitExpr(E); @@ -1402,6 +1306,11 @@ void ASTStmtReader::VisitSubstNonTypeTemplateParmPackExpr( E->NameLoc = ReadSourceLocation(Record, Idx); } +void ASTStmtReader::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { + VisitExpr(E); + E->Temporary = Reader.ReadSubExpr(); +} + void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); Idx++; // skip ID @@ -1409,6 +1318,43 @@ void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) { } //===----------------------------------------------------------------------===// +// Microsoft Expressions and Statements +//===----------------------------------------------------------------------===// +void ASTStmtReader::VisitCXXUuidofExpr(CXXUuidofExpr *E) { + VisitExpr(E); + E->setSourceRange(ReadSourceRange(Record, Idx)); + if (E->isTypeOperand()) { // __uuidof(ComType) + E->setTypeOperandSourceInfo( + GetTypeSourceInfo(Record, Idx)); + return; + } + + // __uuidof(expr) + E->setExprOperand(Reader.ReadSubExpr()); +} + +void ASTStmtReader::VisitSEHExceptStmt(SEHExceptStmt *S) { + VisitStmt(S); + S->Loc = ReadSourceLocation(Record, Idx); + S->Children[SEHExceptStmt::FILTER_EXPR] = Reader.ReadSubStmt(); + S->Children[SEHExceptStmt::BLOCK] = Reader.ReadSubStmt(); +} + +void ASTStmtReader::VisitSEHFinallyStmt(SEHFinallyStmt *S) { + VisitStmt(S); + S->Loc = ReadSourceLocation(Record, Idx); + S->Block = Reader.ReadSubStmt(); +} + +void ASTStmtReader::VisitSEHTryStmt(SEHTryStmt *S) { + VisitStmt(S); + S->IsCXXTry = Record[Idx++]; + S->TryLoc = ReadSourceLocation(Record, Idx); + S->Children[SEHTryStmt::TRY] = Reader.ReadSubStmt(); + S->Children[SEHTryStmt::HANDLER] = Reader.ReadSubStmt(); +} + +//===----------------------------------------------------------------------===// // CUDA Expressions and Statements //===----------------------------------------------------------------------===// @@ -1417,6 +1363,20 @@ void ASTStmtReader::VisitCUDAKernelCallExpr(CUDAKernelCallExpr *E) { E->setConfig(cast<CallExpr>(Reader.ReadSubExpr())); } +//===----------------------------------------------------------------------===// +// OpenCL Expressions and Statements. +//===----------------------------------------------------------------------===// +void ASTStmtReader::VisitAsTypeExpr(AsTypeExpr *E) { + VisitExpr(E); + E->BuiltinLoc = ReadSourceLocation(Record, Idx); + E->RParenLoc = ReadSourceLocation(Record, Idx); + E->SrcExpr = Reader.ReadSubExpr(); +} + +//===----------------------------------------------------------------------===// +// ASTReader Implementation +//===----------------------------------------------------------------------===// + Stmt *ASTReader::ReadStmt(PerFileData &F) { switch (ReadingKind) { case Read_Decl: @@ -1784,6 +1744,12 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) { case EXPR_OBJC_ISA: S = new (Context) ObjCIsaExpr(Empty); break; + case EXPR_OBJC_INDIRECT_COPY_RESTORE: + S = new (Context) ObjCIndirectCopyRestoreExpr(Empty); + break; + case EXPR_OBJC_BRIDGED_CAST: + S = new (Context) ObjCBridgedCastExpr(Empty); + break; case STMT_OBJC_FOR_COLLECTION: S = new (Context) ObjCForCollectionStmt(Empty); break; @@ -1804,7 +1770,18 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) { case STMT_OBJC_AT_THROW: S = new (Context) ObjCAtThrowStmt(Empty); break; - + case STMT_OBJC_AUTORELEASE_POOL: + S = new (Context) ObjCAutoreleasePoolStmt(Empty); + break; + case STMT_SEH_EXCEPT: + S = new (Context) SEHExceptStmt(Empty); + break; + case STMT_SEH_FINALLY: + S = new (Context) SEHFinallyStmt(Empty); + break; + case STMT_SEH_TRY: + S = new (Context) SEHTryStmt(Empty); + break; case STMT_CXX_CATCH: S = new (Context) CXXCatchStmt(Empty); break; @@ -1978,10 +1955,18 @@ Stmt *ASTReader::ReadStmtFromStream(PerFileData &F) { S = new (Context) SizeOfPackExpr(Empty); break; + case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM: + S = new (Context) SubstNonTypeTemplateParmExpr(Empty); + break; + case EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK: S = new (Context) SubstNonTypeTemplateParmPackExpr(Empty); break; + case EXPR_MATERIALIZE_TEMPORARY: + S = new (Context) MaterializeTemporaryExpr(Empty); + break; + case EXPR_OPAQUE_VALUE: { unsigned key = Record[ASTStmtReader::NumExprFields]; OpaqueValueExpr *&expr = OpaqueValueExprs[key]; diff --git a/lib/Serialization/ASTWriter.cpp b/lib/Serialization/ASTWriter.cpp index ba9032e0d33e..39f2fbddf80e 100644 --- a/lib/Serialization/ASTWriter.cpp +++ b/lib/Serialization/ASTWriter.cpp @@ -170,6 +170,7 @@ void ASTTypeWriter::VisitFunctionType(const FunctionType *T) { Record.push_back(C.getRegParm()); // FIXME: need to stabilize encoding of calling convention... Record.push_back(C.getCC()); + Record.push_back(C.getProducesResult()); } void ASTTypeWriter::VisitFunctionNoProtoType(const FunctionNoProtoType *T) { @@ -780,13 +781,15 @@ void ASTWriter::WriteBlockInfoBlock() { RECORD(FP_PRAGMA_OPTIONS); RECORD(OPENCL_EXTENSIONS); RECORD(DELEGATING_CTORS); + RECORD(FILE_SOURCE_LOCATION_OFFSETS); + RECORD(KNOWN_NAMESPACES); // SourceManager Block. BLOCK(SOURCE_MANAGER_BLOCK); RECORD(SM_SLOC_FILE_ENTRY); RECORD(SM_SLOC_BUFFER_ENTRY); RECORD(SM_SLOC_BUFFER_BLOB); - RECORD(SM_SLOC_INSTANTIATION_ENTRY); + RECORD(SM_SLOC_EXPANSION_ENTRY); RECORD(SM_LINE_TABLE); // Preprocessor Block. @@ -893,7 +896,7 @@ void ASTWriter::WriteBlockInfoBlock() { AddStmtsExprs(Stream, Record); BLOCK(PREPROCESSOR_DETAIL_BLOCK); - RECORD(PPD_MACRO_INSTANTIATION); + RECORD(PPD_MACRO_EXPANSION); RECORD(PPD_MACRO_DEFINITION); RECORD(PPD_INCLUSION_DIRECTIVE); @@ -1109,6 +1112,8 @@ void ASTWriter::WriteLanguageOptions(const LangOptions &LangOpts) { Record.push_back(LangOpts.ElideConstructors); Record.push_back(LangOpts.SpellChecking); Record.push_back(LangOpts.MRTD); + Record.push_back(LangOpts.ObjCAutoRefCount); + Record.push_back(LangOpts.ObjCInferRelatedReturnType); Stream.EmitRecord(LANGUAGE_OPTIONS, Record); } @@ -1169,8 +1174,8 @@ void ASTWriter::WriteStatCache(MemorizeStatCalls &StatCalls) { for (MemorizeStatCalls::iterator Stat = StatCalls.begin(), StatEnd = StatCalls.end(); Stat != StatEnd; ++Stat, ++NumStatEntries) { - const char *Filename = Stat->first(); - Generator.insert(Filename, Stat->second); + llvm::StringRef Filename = Stat->first(); + Generator.insert(Filename.data(), Stat->second); } // Create the on-disk hash table in a buffer. @@ -1245,12 +1250,12 @@ static unsigned CreateSLocBufferBlobAbbrev(llvm::BitstreamWriter &Stream) { return Stream.EmitAbbrev(Abbrev); } -/// \brief Create an abbreviation for the SLocEntry that refers to an -/// buffer. -static unsigned CreateSLocInstantiationAbbrev(llvm::BitstreamWriter &Stream) { +/// \brief Create an abbreviation for the SLocEntry that refers to a macro +/// expansion. +static unsigned CreateSLocExpansionAbbrev(llvm::BitstreamWriter &Stream) { using namespace llvm; BitCodeAbbrev *Abbrev = new BitCodeAbbrev(); - Abbrev->Add(BitCodeAbbrevOp(SM_SLOC_INSTANTIATION_ENTRY)); + Abbrev->Add(BitCodeAbbrevOp(SM_SLOC_EXPANSION_ENTRY)); Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Offset Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Spelling location Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Start location @@ -1409,7 +1414,7 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, unsigned SLocFileAbbrv = CreateSLocFileAbbrev(Stream); unsigned SLocBufferAbbrv = CreateSLocBufferAbbrev(Stream); unsigned SLocBufferBlobAbbrv = CreateSLocBufferBlobAbbrev(Stream); - unsigned SLocInstantiationAbbrv = CreateSLocInstantiationAbbrev(Stream); + unsigned SLocExpansionAbbrv = CreateSLocExpansionAbbrev(Stream); // Write the line table. if (SourceMgr.hasLineTable()) { @@ -1474,7 +1479,7 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, } else Code = SM_SLOC_BUFFER_ENTRY; } else - Code = SM_SLOC_INSTANTIATION_ENTRY; + Code = SM_SLOC_EXPANSION_ENTRY; Record.clear(); Record.push_back(Code); @@ -1534,7 +1539,7 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, PreloadSLocs.push_back(BaseSLocID + SLocEntryOffsets.size()); } } else { - // The source location entry is an instantiation. + // The source location entry is a macro expansion. const SrcMgr::InstantiationInfo &Inst = SLoc->getInstantiation(); Record.push_back(Inst.getSpellingLoc().getRawEncoding()); Record.push_back(Inst.getInstantiationLocStart().getRawEncoding()); @@ -1545,7 +1550,7 @@ void ASTWriter::WriteSourceManagerBlock(SourceManager &SourceMgr, if (I + 1 != N) NextOffset = SourceMgr.getSLocEntry(I + 1).getOffset(); Record.push_back(NextOffset - SLoc->getOffset() - 1); - Stream.EmitRecordWithAbbrev(SLocInstantiationAbbrv, Record); + Stream.EmitRecordWithAbbrev(SLocExpansionAbbrv, Record); } } @@ -1795,13 +1800,13 @@ void ASTWriter::WritePreprocessorDetail(PreprocessingRecord &PPRec) { SerializationListener->SerializedPreprocessedEntity(*E, Stream.GetCurrentBitNo()); - if (MacroInstantiation *MI = dyn_cast<MacroInstantiation>(*E)) { + if (MacroExpansion *ME = dyn_cast<MacroExpansion>(*E)) { Record.push_back(IndexBase + NumPreprocessingRecords++); - AddSourceLocation(MI->getSourceRange().getBegin(), Record); - AddSourceLocation(MI->getSourceRange().getEnd(), Record); - AddIdentifierRef(MI->getName(), Record); - Record.push_back(getMacroDefinitionID(MI->getDefinition())); - Stream.EmitRecord(PPD_MACRO_INSTANTIATION, Record); + AddSourceLocation(ME->getSourceRange().getBegin(), Record); + AddSourceLocation(ME->getSourceRange().getEnd(), Record); + AddIdentifierRef(ME->getName(), Record); + Record.push_back(getMacroDefinitionID(ME->getDefinition())); + Stream.EmitRecord(PPD_MACRO_EXPANSION, Record); continue; } @@ -2842,6 +2847,16 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, AddDeclRef(Context.getcudaConfigureCallDecl(), CUDASpecialDeclRefs); } + // Build a record containing all of the known namespaces. + RecordData KnownNamespaces; + for (llvm::DenseMap<NamespaceDecl*, bool>::iterator + I = SemaRef.KnownNamespaces.begin(), + IEnd = SemaRef.KnownNamespaces.end(); + I != IEnd; ++I) { + if (!I->second) + AddDeclRef(I->first, KnownNamespaces); + } + // Write the remaining AST contents. RecordData Record; Stream.EnterSubblock(AST_BLOCK_ID, 5); @@ -2951,6 +2966,10 @@ void ASTWriter::WriteASTCore(Sema &SemaRef, MemorizeStatCalls *StatCalls, if (!DelegatingCtorDecls.empty()) Stream.EmitRecord(DELEGATING_CTORS, DelegatingCtorDecls); + // Write the known namespaces. + if (!KnownNamespaces.empty()) + Stream.EmitRecord(KNOWN_NAMESPACES, KnownNamespaces); + // Some simple statistics Record.clear(); Record.push_back(NumStatements); @@ -3675,6 +3694,14 @@ void ASTWriter::AddTemplateName(TemplateName Name, RecordDataImpl &Record) { Record.push_back(DepT->getOperator()); break; } + + case TemplateName::SubstTemplateTemplateParm: { + SubstTemplateTemplateParmStorage *subst + = Name.getAsSubstTemplateTemplateParm(); + AddDeclRef(subst->getParameter(), Record); + AddTemplateName(subst->getReplacement(), Record); + break; + } case TemplateName::SubstTemplateTemplateParmPack: { SubstTemplateTemplateParmPackStorage *SubstPack diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 7c24088c65ba..2b8349495751 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -645,6 +645,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { Record.push_back(D->isExceptionVariable()); Record.push_back(D->isNRVOVariable()); Record.push_back(D->isCXXForRangeDecl()); + Record.push_back(D->isARCPseudoStrong()); Record.push_back(D->getInit() ? 1 : 0); if (D->getInit()) Writer.AddStmt(D->getInit()); @@ -670,7 +671,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { D->RedeclLink.getNext() == D && !D->hasCXXDirectInitializer() && D->getInit() == 0 && - !ParmVarDecl::classofKind(D->getKind()) && + !isa<ParmVarDecl>(D) && !SpecInfo) AbbrevToUse = Writer.getDeclVarAbbrev(); @@ -695,6 +696,8 @@ void ASTDeclWriter::VisitParmVarDecl(ParmVarDecl *D) { Writer.AddStmt(D->getUninstantiatedDefaultArg()); Code = serialization::DECL_PARM_VAR; + assert(!D->isARCPseudoStrong()); // can be true of ImplicitParamDecl + // If the assumptions about the DECL_PARM_VAR abbrev are true, use it. Here // we dynamically check for the properties that we optimize for, but don't // know are true of all PARM_VAR_DECLs. @@ -1426,6 +1429,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(0)); // isExceptionVariable Abv->Add(BitCodeAbbrevOp(0)); // isNRVOVariable Abv->Add(BitCodeAbbrevOp(0)); // isCXXForRangeDecl + Abv->Add(BitCodeAbbrevOp(0)); // isARCPseudoStrong Abv->Add(BitCodeAbbrevOp(0)); // HasInit Abv->Add(BitCodeAbbrevOp(0)); // HasMemberSpecializationInfo // ParmVarDecl @@ -1498,6 +1502,7 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isExceptionVariable Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isNRVOVariable Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isCXXForRangeDecl + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // isARCPseudoStrong Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasInit Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); // HasMemberSpecInfo // Type Source Info @@ -1512,8 +1517,9 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { //Stmt //Expr Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Type - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //IsTypeDependent - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //IsValueDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //TypeDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind @@ -1531,8 +1537,9 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { //Stmt //Expr Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Type - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //IsTypeDependent - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //IsValueDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //TypeDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind @@ -1548,8 +1555,9 @@ void ASTWriter::WriteDeclsBlockAbbrevs() { //Stmt //Expr Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Type - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //IsTypeDependent - Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //IsValueDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //TypeDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //ValueDependent + Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //InstantiationDependent Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 1)); //UnexpandedParamPack Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetValueKind Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); //GetObjectKind diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp index 00e240494645..1d73ed447b4f 100644 --- a/lib/Serialization/ASTWriterStmt.cpp +++ b/lib/Serialization/ASTWriterStmt.cpp @@ -39,134 +39,9 @@ namespace clang { AddExplicitTemplateArgumentList(const ExplicitTemplateArgumentList &Args); void VisitStmt(Stmt *S); - void VisitNullStmt(NullStmt *S); - void VisitCompoundStmt(CompoundStmt *S); - void VisitSwitchCase(SwitchCase *S); - void VisitCaseStmt(CaseStmt *S); - void VisitDefaultStmt(DefaultStmt *S); - void VisitLabelStmt(LabelStmt *S); - void VisitIfStmt(IfStmt *S); - void VisitSwitchStmt(SwitchStmt *S); - void VisitWhileStmt(WhileStmt *S); - void VisitDoStmt(DoStmt *S); - void VisitForStmt(ForStmt *S); - void VisitGotoStmt(GotoStmt *S); - void VisitIndirectGotoStmt(IndirectGotoStmt *S); - void VisitContinueStmt(ContinueStmt *S); - void VisitBreakStmt(BreakStmt *S); - void VisitReturnStmt(ReturnStmt *S); - void VisitDeclStmt(DeclStmt *S); - void VisitAsmStmt(AsmStmt *S); - void VisitExpr(Expr *E); - void VisitPredefinedExpr(PredefinedExpr *E); - void VisitDeclRefExpr(DeclRefExpr *E); - void VisitIntegerLiteral(IntegerLiteral *E); - void VisitFloatingLiteral(FloatingLiteral *E); - void VisitImaginaryLiteral(ImaginaryLiteral *E); - void VisitStringLiteral(StringLiteral *E); - void VisitCharacterLiteral(CharacterLiteral *E); - void VisitParenExpr(ParenExpr *E); - void VisitParenListExpr(ParenListExpr *E); - void VisitUnaryOperator(UnaryOperator *E); - void VisitOffsetOfExpr(OffsetOfExpr *E); - void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); - void VisitArraySubscriptExpr(ArraySubscriptExpr *E); - void VisitCallExpr(CallExpr *E); - void VisitMemberExpr(MemberExpr *E); - void VisitCastExpr(CastExpr *E); - void VisitBinaryOperator(BinaryOperator *E); - void VisitCompoundAssignOperator(CompoundAssignOperator *E); - void VisitConditionalOperator(ConditionalOperator *E); - void VisitBinaryConditionalOperator(BinaryConditionalOperator *E); - void VisitImplicitCastExpr(ImplicitCastExpr *E); - void VisitExplicitCastExpr(ExplicitCastExpr *E); - void VisitCStyleCastExpr(CStyleCastExpr *E); - void VisitCompoundLiteralExpr(CompoundLiteralExpr *E); - void VisitExtVectorElementExpr(ExtVectorElementExpr *E); - void VisitInitListExpr(InitListExpr *E); - void VisitDesignatedInitExpr(DesignatedInitExpr *E); - void VisitImplicitValueInitExpr(ImplicitValueInitExpr *E); - void VisitVAArgExpr(VAArgExpr *E); - void VisitAddrLabelExpr(AddrLabelExpr *E); - void VisitStmtExpr(StmtExpr *E); - void VisitChooseExpr(ChooseExpr *E); - void VisitGNUNullExpr(GNUNullExpr *E); - void VisitShuffleVectorExpr(ShuffleVectorExpr *E); - void VisitBlockExpr(BlockExpr *E); - void VisitBlockDeclRefExpr(BlockDeclRefExpr *E); - void VisitGenericSelectionExpr(GenericSelectionExpr *E); - - // Objective-C Expressions - void VisitObjCStringLiteral(ObjCStringLiteral *E); - void VisitObjCEncodeExpr(ObjCEncodeExpr *E); - void VisitObjCSelectorExpr(ObjCSelectorExpr *E); - void VisitObjCProtocolExpr(ObjCProtocolExpr *E); - void VisitObjCIvarRefExpr(ObjCIvarRefExpr *E); - void VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E); - void VisitObjCMessageExpr(ObjCMessageExpr *E); - void VisitObjCIsaExpr(ObjCIsaExpr *E); - - // Objective-C Statements - void VisitObjCForCollectionStmt(ObjCForCollectionStmt *); - void VisitObjCAtCatchStmt(ObjCAtCatchStmt *); - void VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *); - void VisitObjCAtTryStmt(ObjCAtTryStmt *); - void VisitObjCAtSynchronizedStmt(ObjCAtSynchronizedStmt *); - void VisitObjCAtThrowStmt(ObjCAtThrowStmt *); - - // C++ Statements - void VisitCXXCatchStmt(CXXCatchStmt *S); - void VisitCXXTryStmt(CXXTryStmt *S); - void VisitCXXForRangeStmt(CXXForRangeStmt *); - - void VisitCXXOperatorCallExpr(CXXOperatorCallExpr *E); - void VisitCXXMemberCallExpr(CXXMemberCallExpr *E); - void VisitCXXConstructExpr(CXXConstructExpr *E); - void VisitCXXTemporaryObjectExpr(CXXTemporaryObjectExpr *E); - void VisitCXXNamedCastExpr(CXXNamedCastExpr *E); - void VisitCXXStaticCastExpr(CXXStaticCastExpr *E); - void VisitCXXDynamicCastExpr(CXXDynamicCastExpr *E); - void VisitCXXReinterpretCastExpr(CXXReinterpretCastExpr *E); - void VisitCXXConstCastExpr(CXXConstCastExpr *E); - void VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E); - void VisitCXXBoolLiteralExpr(CXXBoolLiteralExpr *E); - void VisitCXXNullPtrLiteralExpr(CXXNullPtrLiteralExpr *E); - void VisitCXXTypeidExpr(CXXTypeidExpr *E); - void VisitCXXUuidofExpr(CXXUuidofExpr *E); - void VisitCXXThisExpr(CXXThisExpr *E); - void VisitCXXThrowExpr(CXXThrowExpr *E); - void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E); - void VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E); - - void VisitCXXScalarValueInitExpr(CXXScalarValueInitExpr *E); - void VisitCXXNewExpr(CXXNewExpr *E); - void VisitCXXDeleteExpr(CXXDeleteExpr *E); - void VisitCXXPseudoDestructorExpr(CXXPseudoDestructorExpr *E); - - void VisitExprWithCleanups(ExprWithCleanups *E); - void VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E); - void VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E); - void VisitCXXUnresolvedConstructExpr(CXXUnresolvedConstructExpr *E); - - void VisitOverloadExpr(OverloadExpr *E); - void VisitUnresolvedMemberExpr(UnresolvedMemberExpr *E); - void VisitUnresolvedLookupExpr(UnresolvedLookupExpr *E); - - void VisitUnaryTypeTraitExpr(UnaryTypeTraitExpr *E); - void VisitBinaryTypeTraitExpr(BinaryTypeTraitExpr *E); - void VisitArrayTypeTraitExpr(ArrayTypeTraitExpr *E); - void VisitExpressionTraitExpr(ExpressionTraitExpr *E); - void VisitCXXNoexceptExpr(CXXNoexceptExpr *E); - void VisitPackExpansionExpr(PackExpansionExpr *E); - void VisitSizeOfPackExpr(SizeOfPackExpr *E); - void VisitSubstNonTypeTemplateParmPackExpr( - SubstNonTypeTemplateParmPackExpr *E); - void VisitOpaqueValueExpr(OpaqueValueExpr *E); - - // CUDA Expressions - void VisitCUDAKernelCallExpr(CUDAKernelCallExpr *E); - - void VisitAsTypeExpr(AsTypeExpr *E); +#define STMT(Type, Base) \ + void Visit##Type(Type *); +#include "clang/AST/StmtNodes.inc" }; } @@ -371,6 +246,7 @@ void ASTStmtWriter::VisitExpr(Expr *E) { Writer.AddTypeRef(E->getType(), Record); Record.push_back(E->isTypeDependent()); Record.push_back(E->isValueDependent()); + Record.push_back(E->isInstantiationDependent()); Record.push_back(E->containsUnexpandedParameterPack()); Record.push_back(E->getValueKind()); Record.push_back(E->getObjectKind()); @@ -606,6 +482,22 @@ void ASTStmtWriter::VisitObjCIsaExpr(ObjCIsaExpr *E) { Code = serialization::EXPR_OBJC_ISA; } +void ASTStmtWriter:: +VisitObjCIndirectCopyRestoreExpr(ObjCIndirectCopyRestoreExpr *E) { + VisitExpr(E); + Writer.AddStmt(E->getSubExpr()); + Record.push_back(E->shouldCopy()); + Code = serialization::EXPR_OBJC_INDIRECT_COPY_RESTORE; +} + +void ASTStmtWriter::VisitObjCBridgedCastExpr(ObjCBridgedCastExpr *E) { + VisitExplicitCastExpr(E); + Writer.AddSourceLocation(E->getLParenLoc(), Record); + Writer.AddSourceLocation(E->getBridgeKeywordLoc(), Record); + Record.push_back(E->getBridgeKind()); // FIXME: Stable encoding + Code = serialization::EXPR_OBJC_BRIDGED_CAST; +} + void ASTStmtWriter::VisitCastExpr(CastExpr *E) { VisitExpr(E); Record.push_back(E->path_size()); @@ -914,6 +806,7 @@ void ASTStmtWriter::VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *E) { void ASTStmtWriter::VisitObjCMessageExpr(ObjCMessageExpr *E) { VisitExpr(E); Record.push_back(E->getNumArgs()); + Record.push_back(E->isDelegateInitCall()); Record.push_back((unsigned)E->getReceiverKind()); // FIXME: stable encoding switch (E->getReceiverKind()) { case ObjCMessageExpr::Instance: @@ -973,6 +866,12 @@ void ASTStmtWriter::VisitObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { Code = serialization::STMT_OBJC_FINALLY; } +void ASTStmtWriter::VisitObjCAutoreleasePoolStmt(ObjCAutoreleasePoolStmt *S) { + Writer.AddStmt(S->getSubStmt()); + Writer.AddSourceLocation(S->getAtLoc(), Record); + Code = serialization::STMT_OBJC_AUTORELEASE_POOL; +} + void ASTStmtWriter::VisitObjCAtTryStmt(ObjCAtTryStmt *S) { Record.push_back(S->getNumCatchStmts()); Record.push_back(S->getFinallyStmt() != 0); @@ -1123,18 +1022,6 @@ void ASTStmtWriter::VisitCXXTypeidExpr(CXXTypeidExpr *E) { } } -void ASTStmtWriter::VisitCXXUuidofExpr(CXXUuidofExpr *E) { - VisitExpr(E); - Writer.AddSourceRange(E->getSourceRange(), Record); - if (E->isTypeOperand()) { - Writer.AddTypeSourceInfo(E->getTypeOperandSourceInfo(), Record); - Code = serialization::EXPR_CXX_UUIDOF_TYPE; - } else { - Writer.AddStmt(E->getExprOperand()); - Code = serialization::EXPR_CXX_UUIDOF_EXPR; - } -} - void ASTStmtWriter::VisitCXXThisExpr(CXXThisExpr *E) { VisitExpr(E); Writer.AddSourceLocation(E->getLocation(), Record); @@ -1146,6 +1033,7 @@ void ASTStmtWriter::VisitCXXThrowExpr(CXXThrowExpr *E) { VisitExpr(E); Writer.AddSourceLocation(E->getThrowLoc(), Record); Writer.AddStmt(E->getSubExpr()); + Record.push_back(E->isThrownVariableInScope()); Code = serialization::EXPR_CXX_THROW; } @@ -1408,15 +1296,30 @@ void ASTStmtWriter::VisitSizeOfPackExpr(SizeOfPackExpr *E) { Code = serialization::EXPR_SIZEOF_PACK; } +void ASTStmtWriter::VisitSubstNonTypeTemplateParmExpr( + SubstNonTypeTemplateParmExpr *E) { + VisitExpr(E); + Writer.AddDeclRef(E->getParameter(), Record); + Writer.AddSourceLocation(E->getNameLoc(), Record); + Writer.AddStmt(E->getReplacement()); + Code = serialization::EXPR_SUBST_NON_TYPE_TEMPLATE_PARM; +} + void ASTStmtWriter::VisitSubstNonTypeTemplateParmPackExpr( SubstNonTypeTemplateParmPackExpr *E) { VisitExpr(E); - Writer.AddDeclRef(E->Param, Record); + Writer.AddDeclRef(E->getParameterPack(), Record); Writer.AddTemplateArgument(E->getArgumentPack(), Record); - Writer.AddSourceLocation(E->NameLoc, Record); + Writer.AddSourceLocation(E->getParameterPackLocation(), Record); Code = serialization::EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK; } +void ASTStmtWriter::VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *E) { + VisitExpr(E); + Writer.AddStmt(E->Temporary); + Code = serialization::EXPR_MATERIALIZE_TEMPORARY; +} + void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) { VisitExpr(E); Record.push_back(Writer.getOpaqueValueID(E)); @@ -1439,11 +1342,52 @@ void ASTStmtWriter::VisitCUDAKernelCallExpr(CUDAKernelCallExpr *E) { //===----------------------------------------------------------------------===// void ASTStmtWriter::VisitAsTypeExpr(AsTypeExpr *E) { VisitExpr(E); + Writer.AddSourceLocation(E->getBuiltinLoc(), Record); + Writer.AddSourceLocation(E->getRParenLoc(), Record); Writer.AddStmt(E->getSrcExpr()); Code = serialization::EXPR_ASTYPE; } //===----------------------------------------------------------------------===// +// Microsoft Expressions and Statements. +//===----------------------------------------------------------------------===// +void ASTStmtWriter::VisitCXXUuidofExpr(CXXUuidofExpr *E) { + VisitExpr(E); + Writer.AddSourceRange(E->getSourceRange(), Record); + if (E->isTypeOperand()) { + Writer.AddTypeSourceInfo(E->getTypeOperandSourceInfo(), Record); + Code = serialization::EXPR_CXX_UUIDOF_TYPE; + } else { + Writer.AddStmt(E->getExprOperand()); + Code = serialization::EXPR_CXX_UUIDOF_EXPR; + } +} + +void ASTStmtWriter::VisitSEHExceptStmt(SEHExceptStmt *S) { + VisitStmt(S); + Writer.AddSourceLocation(S->getExceptLoc(), Record); + Writer.AddStmt(S->getFilterExpr()); + Writer.AddStmt(S->getBlock()); + Code = serialization::STMT_SEH_EXCEPT; +} + +void ASTStmtWriter::VisitSEHFinallyStmt(SEHFinallyStmt *S) { + VisitStmt(S); + Writer.AddSourceLocation(S->getFinallyLoc(), Record); + Writer.AddStmt(S->getBlock()); + Code = serialization::STMT_SEH_FINALLY; +} + +void ASTStmtWriter::VisitSEHTryStmt(SEHTryStmt *S) { + VisitStmt(S); + Record.push_back(S->getIsCXXTry()); + Writer.AddSourceLocation(S->getTryLoc(), Record); + Writer.AddStmt(S->getTryBlock()); + Writer.AddStmt(S->getHandler()); + Code = serialization::STMT_SEH_TRY; +} + +//===----------------------------------------------------------------------===// // ASTWriter Implementation //===----------------------------------------------------------------------===// diff --git a/lib/Serialization/ChainedIncludesSource.cpp b/lib/Serialization/ChainedIncludesSource.cpp index da5be957a530..3b7cd23b92ae 100644 --- a/lib/Serialization/ChainedIncludesSource.cpp +++ b/lib/Serialization/ChainedIncludesSource.cpp @@ -185,7 +185,8 @@ ChainedIncludesSource::FindExternalVisibleDeclsByName(const DeclContext *DC, void ChainedIncludesSource::MaterializeVisibleDecls(const DeclContext *DC) { return getFinalReader().MaterializeVisibleDecls(DC); } -bool ChainedIncludesSource::FindExternalLexicalDecls(const DeclContext *DC, +ExternalLoadResult +ChainedIncludesSource::FindExternalLexicalDecls(const DeclContext *DC, bool (*isKindWeWant)(Decl::Kind), llvm::SmallVectorImpl<Decl*> &Result) { return getFinalReader().FindExternalLexicalDecls(DC, isKindWeWant, Result); diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 235b400eb9e3..9fc8163ab8b9 100644 --- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -606,7 +606,7 @@ void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg, continue; // Ignore CF references, which can be toll-free bridged. - if (cocoa::isCFObjectRef(ArgTy)) + if (coreFoundation::isCFObjectRef(ArgTy)) continue; // Generate only one error node to use for all bug reports. diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp index f2f5c1ed44e9..c5dac5d21626 100644 --- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -30,8 +30,11 @@ class CStringChecker : public Checker< eval::Call, check::DeadSymbols, check::RegionChanges > { - mutable llvm::OwningPtr<BugType> BT_Null, BT_Bounds, BT_BoundsWrite, - BT_Overlap, BT_NotCString; + mutable llvm::OwningPtr<BugType> BT_Null, BT_Bounds, + BT_Overlap, BT_NotCString, + BT_AdditionOverflow; + mutable const char *CurrentFunctionDescription; + public: static void *getTag() { static int tag; return &tag; } @@ -91,9 +94,11 @@ public: const MemRegion *MR, SVal strLength); static SVal getCStringLengthForRegion(CheckerContext &C, const GRState *&state, - const Expr *Ex, const MemRegion *MR); + const Expr *Ex, const MemRegion *MR, + bool hypothetical); SVal getCStringLength(CheckerContext &C, const GRState *&state, - const Expr *Ex, SVal Buf) const; + const Expr *Ex, SVal Buf, + bool hypothetical = false) const; const StringLiteral *getCStringLiteral(CheckerContext &C, const GRState *&state, @@ -112,17 +117,29 @@ public: const Expr *S, SVal l) const; const GRState *CheckLocation(CheckerContext &C, const GRState *state, const Expr *S, SVal l, - bool IsDestination = false) const; + const char *message = NULL) const; const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *FirstBuf, - const Expr *SecondBuf = NULL, - bool FirstIsDestination = false) const; + const Expr *SecondBuf, + const char *firstMessage = NULL, + const char *secondMessage = NULL, + bool WarnAboutSize = false) const; + const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *Buf, + const char *message = NULL, + bool WarnAboutSize = false) const { + // This is a convenience override. + return CheckBufferAccess(C, state, Size, Buf, NULL, message, NULL, + WarnAboutSize); + } const GRState *CheckOverlap(CheckerContext &C, const GRState *state, const Expr *Size, const Expr *First, const Expr *Second) const; void emitOverlapBug(CheckerContext &C, const GRState *state, const Stmt *First, const Stmt *Second) const; + const GRState *checkAdditionOverflow(CheckerContext &C, const GRState *state, + NonLoc left, NonLoc right) const; }; class CStringLength { @@ -176,10 +193,14 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C, BT_Null.reset(new BuiltinBug("API", "Null pointer argument in call to byte string function")); + llvm::SmallString<80> buf; + llvm::raw_svector_ostream os(buf); + assert(CurrentFunctionDescription); + os << "Null pointer argument in call to " << CurrentFunctionDescription; + // Generate a report for this bug. BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Null.get()); - EnhancedBugReport *report = new EnhancedBugReport(*BT, - BT->getDescription(), N); + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); report->addRange(S->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, S); @@ -196,7 +217,7 @@ const GRState *CStringChecker::checkNonNull(CheckerContext &C, const GRState *CStringChecker::CheckLocation(CheckerContext &C, const GRState *state, const Expr *S, SVal l, - bool IsDestination) const { + const char *warningMsg) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; @@ -216,7 +237,8 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, // Get the size of the array. const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); SValBuilder &svalBuilder = C.getSValBuilder(); - SVal Extent = svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + SVal Extent = + svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); DefinedOrUnknownSVal Size = cast<DefinedOrUnknownSVal>(Extent); // Get the index of the accessed element. @@ -229,28 +251,32 @@ const GRState *CStringChecker::CheckLocation(CheckerContext &C, if (!N) return NULL; - BuiltinBug *BT; - if (IsDestination) { - if (!BT_BoundsWrite) { - BT_BoundsWrite.reset(new BuiltinBug("Out-of-bound array access", - "Byte string function overflows destination buffer")); - } - BT = static_cast<BuiltinBug*>(BT_BoundsWrite.get()); + if (!BT_Bounds) { + BT_Bounds.reset(new BuiltinBug("Out-of-bound array access", + "Byte string function accesses out-of-bound array element")); + } + BuiltinBug *BT = static_cast<BuiltinBug*>(BT_Bounds.get()); + + // Generate a report for this bug. + RangedBugReport *report; + if (warningMsg) { + report = new RangedBugReport(*BT, warningMsg, N); } else { - if (!BT_Bounds) { - BT_Bounds.reset(new BuiltinBug("Out-of-bound array access", - "Byte string function accesses out-of-bound array element")); - } - BT = static_cast<BuiltinBug*>(BT_Bounds.get()); + assert(CurrentFunctionDescription); + assert(CurrentFunctionDescription[0] != '\0'); + + llvm::SmallString<80> buf; + llvm::raw_svector_ostream os(buf); + os << (char)toupper(CurrentFunctionDescription[0]) + << &CurrentFunctionDescription[1] + << " accesses out-of-bound array element"; + report = new RangedBugReport(*BT, os.str(), N); } // FIXME: It would be nice to eventually make this diagnostic more clear, // e.g., by referencing the original declaration or by saying *why* this // reference is outside the range. - // Generate a report for this bug. - RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); - report->addRange(S->getSourceRange()); C.EmitReport(report); return NULL; @@ -266,13 +292,15 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, const Expr *Size, const Expr *FirstBuf, const Expr *SecondBuf, - bool FirstIsDestination) const { + const char *firstMessage, + const char *secondMessage, + bool WarnAboutSize) const { // If a previous check has failed, propagate the failure. if (!state) return NULL; SValBuilder &svalBuilder = C.getSValBuilder(); - ASTContext &Ctx = C.getASTContext(); + ASTContext &Ctx = svalBuilder.getContext(); QualType sizeTy = Size->getType(); QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); @@ -284,6 +312,8 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, return NULL; // Get the access length and make sure it is known. + // FIXME: This assumes the caller has already checked that the access length + // is positive. And that it's unsigned. SVal LengthVal = state->getSVal(Size); NonLoc *Length = dyn_cast<NonLoc>(&LengthVal); if (!Length) @@ -297,9 +327,11 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, // Check that the first buffer is sufficiently long. SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + const Expr *warningExpr = (WarnAboutSize ? Size : FirstBuf); + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy); - state = CheckLocation(C, state, FirstBuf, BufEnd, FirstIsDestination); + state = CheckLocation(C, state, warningExpr, BufEnd, firstMessage); // If the buffer isn't large enough, abort. if (!state) @@ -315,9 +347,11 @@ const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType()); if (Loc *BufLoc = dyn_cast<Loc>(&BufStart)) { + const Expr *warningExpr = (WarnAboutSize ? Size : SecondBuf); + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy); - state = CheckLocation(C, state, SecondBuf, BufEnd); + state = CheckLocation(C, state, warningExpr, BufEnd, secondMessage); } } @@ -368,8 +402,7 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, state = stateFalse; // Which value comes first? - ASTContext &Ctx = svalBuilder.getContext(); - QualType cmpTy = Ctx.IntTy; + QualType cmpTy = svalBuilder.getConditionType(); SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, *firstLoc, *secondLoc, cmpTy); DefinedOrUnknownSVal *reverseTest = dyn_cast<DefinedOrUnknownSVal>(&reverse); @@ -402,8 +435,10 @@ const GRState *CStringChecker::CheckOverlap(CheckerContext &C, // Convert the first buffer's start address to char*. // Bail out if the cast fails. + ASTContext &Ctx = svalBuilder.getContext(); QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); - SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First->getType()); + SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, + First->getType()); Loc *FirstStartLoc = dyn_cast<Loc>(&FirstStart); if (!FirstStartLoc) return state; @@ -454,12 +489,78 @@ void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, C.EmitReport(report); } +const GRState *CStringChecker::checkAdditionOverflow(CheckerContext &C, + const GRState *state, + NonLoc left, + NonLoc right) const { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + SValBuilder &svalBuilder = C.getSValBuilder(); + BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); + + QualType sizeTy = svalBuilder.getContext().getSizeType(); + const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); + NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); + + SVal maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, + sizeTy); + + if (maxMinusRight.isUnknownOrUndef()) { + // Try switching the operands. (The order of these two assignments is + // important!) + maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left, + sizeTy); + left = right; + } + + if (NonLoc *maxMinusRightNL = dyn_cast<NonLoc>(&maxMinusRight)) { + QualType cmpTy = svalBuilder.getConditionType(); + // If left > max - right, we have an overflow. + SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, + *maxMinusRightNL, cmpTy); + + const GRState *stateOverflow, *stateOkay; + llvm::tie(stateOverflow, stateOkay) = + state->assume(cast<DefinedOrUnknownSVal>(willOverflow)); + + if (stateOverflow && !stateOkay) { + // We have an overflow. Emit a bug report. + ExplodedNode *N = C.generateSink(stateOverflow); + if (!N) + return NULL; + + if (!BT_AdditionOverflow) + BT_AdditionOverflow.reset(new BuiltinBug("API", + "Sum of expressions causes overflow")); + + // This isn't a great error message, but this should never occur in real + // code anyway -- you'd have to create a buffer longer than a size_t can + // represent, which is sort of a contradiction. + const char *warning = + "This expression will create a string whose length is too big to " + "be represented as a size_t"; + + // Generate a report for this bug. + BugReport *report = new BugReport(*BT_AdditionOverflow, warning, N); + C.EmitReport(report); + + return NULL; + } + + // From now on, assume an overflow didn't occur. + assert(stateOkay); + state = stateOkay; + } + + return state; +} + const GRState *CStringChecker::setCStringLength(const GRState *state, const MemRegion *MR, SVal strLength) { assert(!strLength.isUndef() && "Attempt to set an undefined string length"); - if (strLength.isUnknown()) - return state; MR = MR->StripCasts(); @@ -474,7 +575,8 @@ const GRState *CStringChecker::setCStringLength(const GRState *state, case MemRegion::VarRegionKind: case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: - return state->set<CStringLength>(MR, strLength); + // These are the types we can currently track string lengths for. + break; case MemRegion::ElementRegionKind: // FIXME: Handle element regions by upper-bounding the parent region's @@ -488,16 +590,24 @@ const GRState *CStringChecker::setCStringLength(const GRState *state, // warning for things like strcpy((char[]){'a', 0}, "b"); return state; } + + if (strLength.isUnknown()) + return state->remove<CStringLength>(MR); + + return state->set<CStringLength>(MR, strLength); } SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, const GRState *&state, const Expr *Ex, - const MemRegion *MR) { - // If there's a recorded length, go ahead and return it. - const SVal *Recorded = state->get<CStringLength>(MR); - if (Recorded) - return *Recorded; + const MemRegion *MR, + bool hypothetical) { + if (!hypothetical) { + // If there's a recorded length, go ahead and return it. + const SVal *Recorded = state->get<CStringLength>(MR); + if (Recorded) + return *Recorded; + } // Otherwise, get a new symbol and update the state. unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); @@ -505,12 +615,16 @@ SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, QualType sizeTy = svalBuilder.getContext().getSizeType(); SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(), MR, Ex, sizeTy, Count); - state = state->set<CStringLength>(MR, strLength); + + if (!hypothetical) + state = state->set<CStringLength>(MR, strLength); + return strLength; } SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, - const Expr *Ex, SVal Buf) const { + const Expr *Ex, SVal Buf, + bool hypothetical) const { const MemRegion *MR = Buf.getAsRegion(); if (!MR) { // If we can't get a region, see if it's something we /know/ isn't a @@ -524,8 +638,9 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, llvm::SmallString<120> buf; llvm::raw_svector_ostream os(buf); - os << "Argument to byte string function is the address of the label '" - << Label->getLabel()->getName() + assert(CurrentFunctionDescription); + os << "Argument to " << CurrentFunctionDescription + << " is the address of the label '" << Label->getLabel()->getName() << "', which is not a null-terminated string"; // Generate a report for this bug. @@ -561,7 +676,7 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, case MemRegion::VarRegionKind: case MemRegion::FieldRegionKind: case MemRegion::ObjCIvarRegionKind: - return getCStringLengthForRegion(C, state, Ex, MR); + return getCStringLengthForRegion(C, state, Ex, MR, hypothetical); case MemRegion::CompoundLiteralRegionKind: // FIXME: Can we track this? Is it necessary? return UnknownVal(); @@ -581,7 +696,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, llvm::SmallString<120> buf; llvm::raw_svector_ostream os(buf); - os << "Argument to byte string function is "; + assert(CurrentFunctionDescription); + os << "Argument to " << CurrentFunctionDescription << " is "; if (SummarizeRegion(os, C.getASTContext(), MR)) os << ", which is not a null-terminated string"; @@ -700,12 +816,15 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, const Expr *Size, const Expr *Dest, const Expr *Source, bool Restricted, bool IsMempcpy) const { + CurrentFunctionDescription = "memory copy function"; + // See if the size argument is zero. SVal sizeVal = state->getSVal(Size); QualType sizeTy = Size->getType(); const GRState *stateZeroSize, *stateNonZeroSize; - llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); + llvm::tie(stateZeroSize, stateNonZeroSize) = + assumeZero(C, state, sizeVal, sizeTy); // Get the value of the Dest. SVal destVal = state->getSVal(Dest); @@ -737,8 +856,10 @@ void CStringChecker::evalCopyCommon(CheckerContext &C, return; // Ensure the accesses are valid and that the buffers do not overlap. + const char * const writeWarning = + "Memory copy function overflows destination buffer"; state = CheckBufferAccess(C, state, Size, Dest, Source, - /* FirstIsDst = */ true); + writeWarning, /* sourceWarning = */ NULL); if (Restricted) state = CheckOverlap(C, state, Size, Dest, Source); @@ -824,6 +945,8 @@ void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { // int memcmp(const void *s1, const void *s2, size_t n); + CurrentFunctionDescription = "memory comparison function"; + const Expr *Left = CE->getArg(0); const Expr *Right = CE->getArg(1); const Expr *Size = CE->getArg(2); @@ -861,7 +984,7 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { const GRState *StSameBuf, *StNotSameBuf; llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); - // If the two arguments might be the same buffer, we know the result is zero, + // If the two arguments might be the same buffer, we know the result is 0, // and we only need to check one size. if (StSameBuf) { state = StSameBuf; @@ -902,58 +1025,126 @@ void CStringChecker::evalstrnLength(CheckerContext &C, void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, bool IsStrnlen) const { + CurrentFunctionDescription = "string length function"; const GRState *state = C.getState(); + + if (IsStrnlen) { + const Expr *maxlenExpr = CE->getArg(1); + SVal maxlenVal = state->getSVal(maxlenExpr); + + const GRState *stateZeroSize, *stateNonZeroSize; + llvm::tie(stateZeroSize, stateNonZeroSize) = + assumeZero(C, state, maxlenVal, maxlenExpr->getType()); + + // If the size can be zero, the result will be 0 in that case, and we don't + // have to check the string itself. + if (stateZeroSize) { + SVal zero = C.getSValBuilder().makeZeroVal(CE->getType()); + stateZeroSize = stateZeroSize->BindExpr(CE, zero); + C.addTransition(stateZeroSize); + } + + // If the size is GUARANTEED to be zero, we're done! + if (!stateNonZeroSize) + return; + + // Otherwise, record the assumption that the size is nonzero. + state = stateNonZeroSize; + } + + // Check that the string argument is non-null. const Expr *Arg = CE->getArg(0); SVal ArgVal = state->getSVal(Arg); - // Check that the argument is non-null. state = checkNonNull(C, state, Arg, ArgVal); - if (state) { - SVal strLength = getCStringLength(C, state, Arg, ArgVal); + if (!state) + return; - // If the argument isn't a valid C string, there's no valid state to - // transition to. - if (strLength.isUndef()) - return; + SVal strLength = getCStringLength(C, state, Arg, ArgVal); - // If the check is for strnlen() then bind the return value to no more than - // the maxlen value. - if (IsStrnlen) { - const Expr *maxlenExpr = CE->getArg(1); - SVal maxlenVal = state->getSVal(maxlenExpr); - - NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); - NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); + // If the argument isn't a valid C string, there's no valid state to + // transition to. + if (strLength.isUndef()) + return; - QualType cmpTy = C.getSValBuilder().getContext().IntTy; - const GRState *stateTrue, *stateFalse; - - // Check if the strLength is greater than or equal to the maxlen - llvm::tie(stateTrue, stateFalse) = + DefinedOrUnknownSVal result = UnknownVal(); + + // If the check is for strnlen() then bind the return value to no more than + // the maxlen value. + if (IsStrnlen) { + QualType cmpTy = C.getSValBuilder().getConditionType(); + + // It's a little unfortunate to be getting this again, + // but it's not that expensive... + const Expr *maxlenExpr = CE->getArg(1); + SVal maxlenVal = state->getSVal(maxlenExpr); + + NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); + NonLoc *maxlenValNL = dyn_cast<NonLoc>(&maxlenVal); + + if (strLengthNL && maxlenValNL) { + const GRState *stateStringTooLong, *stateStringNotTooLong; + + // Check if the strLength is greater than the maxlen. + llvm::tie(stateStringTooLong, stateStringNotTooLong) = state->assume(cast<DefinedOrUnknownSVal> - (C.getSValBuilder().evalBinOpNN(state, BO_GE, - *strLengthNL, *maxlenValNL, + (C.getSValBuilder().evalBinOpNN(state, BO_GT, + *strLengthNL, + *maxlenValNL, cmpTy))); - // If the strLength is greater than or equal to the maxlen, set strLength - // to maxlen - if (stateTrue && !stateFalse) { - strLength = maxlenVal; + if (stateStringTooLong && !stateStringNotTooLong) { + // If the string is longer than maxlen, return maxlen. + result = *maxlenValNL; + } else if (stateStringNotTooLong && !stateStringTooLong) { + // If the string is shorter than maxlen, return its length. + result = *strLengthNL; } } - // If getCStringLength couldn't figure out the length, conjure a return - // value, so it can be used in constraints, at least. - if (strLength.isUnknown()) { + if (result.isUnknown()) { + // If we don't have enough information for a comparison, there's + // no guarantee the full string length will actually be returned. + // All we know is the return value is the min of the string length + // and the limit. This is better than nothing. unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); - strLength = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + NonLoc *resultNL = cast<NonLoc>(&result); + + if (strLengthNL) { + state = state->assume(cast<DefinedOrUnknownSVal> + (C.getSValBuilder().evalBinOpNN(state, BO_LE, + *resultNL, + *strLengthNL, + cmpTy)), true); + } + + if (maxlenValNL) { + state = state->assume(cast<DefinedOrUnknownSVal> + (C.getSValBuilder().evalBinOpNN(state, BO_LE, + *resultNL, + *maxlenValNL, + cmpTy)), true); + } } - // Bind the return value. - state = state->BindExpr(CE, strLength); - C.addTransition(state); + } else { + // This is a plain strlen(), not strnlen(). + result = cast<DefinedOrUnknownSVal>(strLength); + + // If we don't know the length of the string, conjure a return + // value, so it can be used in constraints, at least. + if (result.isUnknown()) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + result = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + } } + + // Bind the return value. + assert(!result.isUnknown() && "Should have conjured a value by now"); + state = state->BindExpr(CE, result); + C.addTransition(state); } void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { @@ -999,6 +1190,7 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd, bool isBounded, bool isAppending) const { + CurrentFunctionDescription = "string copy function"; const GRState *state = C.getState(); // Check that the destination is non-null. @@ -1023,76 +1215,240 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, if (strLength.isUndef()) return; + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType cmpTy = svalBuilder.getConditionType(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + + // These two values allow checking two kinds of errors: + // - actual overflows caused by a source that doesn't fit in the destination + // - potential overflows caused by a bound that could exceed the destination + SVal amountCopied = UnknownVal(); + SVal maxLastElementIndex = UnknownVal(); + const char *boundWarning = NULL; + // If the function is strncpy, strncat, etc... it is bounded. if (isBounded) { // Get the max number of characters to copy. const Expr *lenExpr = CE->getArg(2); SVal lenVal = state->getSVal(lenExpr); - // Cast the length to a NonLoc SVal. If it is not a NonLoc then give up. - NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); - if (!strLengthNL) - return; + // Protect against misdeclared strncpy(). + lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType()); - // Cast the max length to a NonLoc SVal. If it is not a NonLoc then give up. + NonLoc *strLengthNL = dyn_cast<NonLoc>(&strLength); NonLoc *lenValNL = dyn_cast<NonLoc>(&lenVal); - if (!lenValNL) - return; - QualType cmpTy = C.getSValBuilder().getContext().IntTy; - const GRState *stateTrue, *stateFalse; - - // Check if the max number to copy is less than the length of the src. - llvm::tie(stateTrue, stateFalse) = - state->assume(cast<DefinedOrUnknownSVal> - (C.getSValBuilder().evalBinOpNN(state, BO_GT, - *strLengthNL, *lenValNL, - cmpTy))); - - if (stateTrue) { - // Max number to copy is less than the length of the src, so the actual - // strLength copied is the max number arg. - strLength = lenVal; - } + // If we know both values, we might be able to figure out how much + // we're copying. + if (strLengthNL && lenValNL) { + const GRState *stateSourceTooLong, *stateSourceNotTooLong; + + // Check if the max number to copy is less than the length of the src. + // If the bound is equal to the source length, strncpy won't null- + // terminate the result! + llvm::tie(stateSourceTooLong, stateSourceNotTooLong) = + state->assume(cast<DefinedOrUnknownSVal> + (svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, + *lenValNL, cmpTy))); + + if (stateSourceTooLong && !stateSourceNotTooLong) { + // Max number to copy is less than the length of the src, so the actual + // strLength copied is the max number arg. + state = stateSourceTooLong; + amountCopied = lenVal; + + } else if (!stateSourceTooLong && stateSourceNotTooLong) { + // The source buffer entirely fits in the bound. + state = stateSourceNotTooLong; + amountCopied = strLength; + } + } + + // We still want to know if the bound is known to be too large. + if (lenValNL) { + if (isAppending) { + // For strncat, the check is strlen(dst) + lenVal < sizeof(dst) + + // Get the string length of the destination. If the destination is + // memory that can't have a string length, we shouldn't be copying + // into it anyway. + SVal dstStrLength = getCStringLength(C, state, Dst, DstVal); + if (dstStrLength.isUndef()) + return; + + if (NonLoc *dstStrLengthNL = dyn_cast<NonLoc>(&dstStrLength)) { + maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Add, + *lenValNL, + *dstStrLengthNL, + sizeTy); + boundWarning = "Size argument is greater than the free space in the " + "destination buffer"; + } + + } else { + // For strncpy, this is just checking that lenVal <= sizeof(dst) + // (Yes, strncpy and strncat differ in how they treat termination. + // strncat ALWAYS terminates, but strncpy doesn't.) + NonLoc one = cast<NonLoc>(svalBuilder.makeIntVal(1, sizeTy)); + maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, + one, sizeTy); + boundWarning = "Size argument is greater than the length of the " + "destination buffer"; + } + } + + // If we couldn't pin down the copy length, at least bound it. + // FIXME: We should actually run this code path for append as well, but + // right now it creates problems with constraints (since we can end up + // trying to pass constraints from symbol to symbol). + if (amountCopied.isUnknown() && !isAppending) { + // Try to get a "hypothetical" string length symbol, which we can later + // set as a real value if that turns out to be the case. + amountCopied = getCStringLength(C, state, lenExpr, srcVal, true); + assert(!amountCopied.isUndef()); + + if (NonLoc *amountCopiedNL = dyn_cast<NonLoc>(&amountCopied)) { + if (lenValNL) { + // amountCopied <= lenVal + SVal copiedLessThanBound = svalBuilder.evalBinOpNN(state, BO_LE, + *amountCopiedNL, + *lenValNL, + cmpTy); + state = state->assume(cast<DefinedOrUnknownSVal>(copiedLessThanBound), + true); + if (!state) + return; + } + + if (strLengthNL) { + // amountCopied <= strlen(source) + SVal copiedLessThanSrc = svalBuilder.evalBinOpNN(state, BO_LE, + *amountCopiedNL, + *strLengthNL, + cmpTy); + state = state->assume(cast<DefinedOrUnknownSVal>(copiedLessThanSrc), + true); + if (!state) + return; + } + } + } + + } else { + // The function isn't bounded. The amount copied should match the length + // of the source buffer. + amountCopied = strLength; } + assert(state); + + // This represents the number of characters copied into the destination + // buffer. (It may not actually be the strlen if the destination buffer + // is not terminated.) + SVal finalStrLength = UnknownVal(); + // If this is an appending function (strcat, strncat...) then set the // string length to strlen(src) + strlen(dst) since the buffer will // ultimately contain both. if (isAppending) { - // Get the string length of the destination, or give up. + // Get the string length of the destination. If the destination is memory + // that can't have a string length, we shouldn't be copying into it anyway. SVal dstStrLength = getCStringLength(C, state, Dst, DstVal); if (dstStrLength.isUndef()) return; - NonLoc *srcStrLengthNL = dyn_cast<NonLoc>(&strLength); + NonLoc *srcStrLengthNL = dyn_cast<NonLoc>(&amountCopied); NonLoc *dstStrLengthNL = dyn_cast<NonLoc>(&dstStrLength); - // If src or dst cast to NonLoc is NULL, give up. - if ((!srcStrLengthNL) || (!dstStrLengthNL)) - return; + // If we know both string lengths, we might know the final string length. + if (srcStrLengthNL && dstStrLengthNL) { + // Make sure the two lengths together don't overflow a size_t. + state = checkAdditionOverflow(C, state, *srcStrLengthNL, *dstStrLengthNL); + if (!state) + return; + + finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL, + *dstStrLengthNL, sizeTy); + } - QualType addTy = C.getSValBuilder().getContext().getSizeType(); + // If we couldn't get a single value for the final string length, + // we can at least bound it by the individual lengths. + if (finalStrLength.isUnknown()) { + // Try to get a "hypothetical" string length symbol, which we can later + // set as a real value if that turns out to be the case. + finalStrLength = getCStringLength(C, state, CE, DstVal, true); + assert(!finalStrLength.isUndef()); + + if (NonLoc *finalStrLengthNL = dyn_cast<NonLoc>(&finalStrLength)) { + if (srcStrLengthNL) { + // finalStrLength >= srcStrLength + SVal sourceInResult = svalBuilder.evalBinOpNN(state, BO_GE, + *finalStrLengthNL, + *srcStrLengthNL, + cmpTy); + state = state->assume(cast<DefinedOrUnknownSVal>(sourceInResult), + true); + if (!state) + return; + } + + if (dstStrLengthNL) { + // finalStrLength >= dstStrLength + SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE, + *finalStrLengthNL, + *dstStrLengthNL, + cmpTy); + state = state->assume(cast<DefinedOrUnknownSVal>(destInResult), + true); + if (!state) + return; + } + } + } - strLength = C.getSValBuilder().evalBinOpNN(state, BO_Add, - *srcStrLengthNL, *dstStrLengthNL, - addTy); + } else { + // Otherwise, this is a copy-over function (strcpy, strncpy, ...), and + // the final string length will match the input string length. + finalStrLength = amountCopied; } + // The final result of the function will either be a pointer past the last + // copied element, or a pointer to the start of the destination buffer. SVal Result = (returnEnd ? UnknownVal() : DstVal); + assert(state); + // If the destination is a MemRegion, try to check for a buffer overflow and // record the new string length. if (loc::MemRegionVal *dstRegVal = dyn_cast<loc::MemRegionVal>(&DstVal)) { - // If the length is known, we can check for an overflow. - if (NonLoc *knownStrLength = dyn_cast<NonLoc>(&strLength)) { - SVal lastElement = - C.getSValBuilder().evalBinOpLN(state, BO_Add, *dstRegVal, - *knownStrLength, Dst->getType()); + QualType ptrTy = Dst->getType(); + + // If we have an exact value on a bounded copy, use that to check for + // overflows, rather than our estimate about how much is actually copied. + if (boundWarning) { + if (NonLoc *maxLastNL = dyn_cast<NonLoc>(&maxLastElementIndex)) { + SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, + *maxLastNL, ptrTy); + state = CheckLocation(C, state, CE->getArg(2), maxLastElement, + boundWarning); + if (!state) + return; + } + } - state = CheckLocation(C, state, Dst, lastElement, /* IsDst = */ true); - if (!state) - return; + // Then, if the final length is known... + if (NonLoc *knownStrLength = dyn_cast<NonLoc>(&finalStrLength)) { + SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, + *knownStrLength, ptrTy); + + // ...and we haven't checked the bound, we'll check the actual copy. + if (!boundWarning) { + const char * const warningMsg = + "String copy function overflows destination buffer"; + state = CheckLocation(C, state, Dst, lastElement, warningMsg); + if (!state) + return; + } // If this is a stpcpy-style copy, the last element is the return value. if (returnEnd) @@ -1107,16 +1463,25 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, // string, but that's still an improvement over blank invalidation. state = InvalidateBuffer(C, state, Dst, *dstRegVal); - // Set the C string length of the destination. - state = setCStringLength(state, dstRegVal->getRegion(), strLength); + // Set the C string length of the destination, if we know it. + if (isBounded && !isAppending) { + // strncpy is annoying in that it doesn't guarantee to null-terminate + // the result string. If the original string didn't fit entirely inside + // the bound (including the null-terminator), we don't know how long the + // result is. + if (amountCopied != strLength) + finalStrLength = UnknownVal(); + } + state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength); } + assert(state); + // If this is a stpcpy-style copy, but we were unable to check for a buffer // overflow, we still need a result. Conjure a return value. if (returnEnd && Result.isUnknown()) { - SValBuilder &svalBuilder = C.getSValBuilder(); unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); - strLength = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + Result = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); } // Set the return value. @@ -1125,29 +1490,30 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, } void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { - //int strcmp(const char *restrict s1, const char *restrict s2); + //int strcmp(const char *s1, const char *s2); evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false); } void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { - //int strncmp(const char *restrict s1, const char *restrict s2, size_t n); + //int strncmp(const char *s1, const char *s2, size_t n); evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false); } void CStringChecker::evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const { - //int strcasecmp(const char *restrict s1, const char *restrict s2); + //int strcasecmp(const char *s1, const char *s2); evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true); } void CStringChecker::evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const { - //int strncasecmp(const char *restrict s1, const char *restrict s2, size_t n); + //int strncasecmp(const char *s1, const char *s2, size_t n); evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true); } void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, bool isBounded, bool ignoreCase) const { + CurrentFunctionDescription = "string comparison function"; const GRState *state = C.getState(); // Check that the first string is non-null @@ -1174,52 +1540,96 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, if (s2Length.isUndef()) return; - // Get the string literal of the first string. - const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val); - if (!s1StrLiteral) - return; - llvm::StringRef s1StrRef = s1StrLiteral->getString(); + // If we know the two buffers are the same, we know the result is 0. + // First, get the two buffers' addresses. Another checker will have already + // made sure they're not undefined. + DefinedOrUnknownSVal LV = cast<DefinedOrUnknownSVal>(s1Val); + DefinedOrUnknownSVal RV = cast<DefinedOrUnknownSVal>(s2Val); + + // See if they are the same. + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); + const GRState *StSameBuf, *StNotSameBuf; + llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); + + // If the two arguments might be the same buffer, we know the result is 0, + // and we only need to check one size. + if (StSameBuf) { + StSameBuf = StSameBuf->BindExpr(CE, svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(StSameBuf); + + // If the two arguments are GUARANTEED to be the same, we're done! + if (!StNotSameBuf) + return; + } + + assert(StNotSameBuf); + state = StNotSameBuf; + + // At this point we can go about comparing the two buffers. + // For now, we only do this if they're both known string literals. - // Get the string literal of the second string. + // Attempt to extract string literals from both expressions. + const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val); const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val); - if (!s2StrLiteral) - return; - llvm::StringRef s2StrRef = s2StrLiteral->getString(); + bool canComputeResult = false; + + if (s1StrLiteral && s2StrLiteral) { + llvm::StringRef s1StrRef = s1StrLiteral->getString(); + llvm::StringRef s2StrRef = s2StrLiteral->getString(); + + if (isBounded) { + // Get the max number of characters to compare. + const Expr *lenExpr = CE->getArg(2); + SVal lenVal = state->getSVal(lenExpr); + + // If the length is known, we can get the right substrings. + if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) { + // Create substrings of each to compare the prefix. + s1StrRef = s1StrRef.substr(0, (size_t)len->getZExtValue()); + s2StrRef = s2StrRef.substr(0, (size_t)len->getZExtValue()); + canComputeResult = true; + } + } else { + // This is a normal, unbounded strcmp. + canComputeResult = true; + } - int result; - if (isBounded) { - // Get the max number of characters to compare. - const Expr *lenExpr = CE->getArg(2); - SVal lenVal = state->getSVal(lenExpr); + if (canComputeResult) { + // Real strcmp stops at null characters. + size_t s1Term = s1StrRef.find('\0'); + if (s1Term != llvm::StringRef::npos) + s1StrRef = s1StrRef.substr(0, s1Term); - // Dynamically cast the length to a ConcreteInt. If it is not a ConcreteInt - // then give up, otherwise get the value and use it as the bounds. - nonloc::ConcreteInt *CI = dyn_cast<nonloc::ConcreteInt>(&lenVal); - if (!CI) - return; - llvm::APSInt lenInt(CI->getValue()); + size_t s2Term = s2StrRef.find('\0'); + if (s2Term != llvm::StringRef::npos) + s2StrRef = s2StrRef.substr(0, s2Term); + + // Use StringRef's comparison methods to compute the actual result. + int result; - // Create substrings of each to compare the prefix. - s1StrRef = s1StrRef.substr(0, (size_t)lenInt.getLimitedValue()); - s2StrRef = s2StrRef.substr(0, (size_t)lenInt.getLimitedValue()); + if (ignoreCase) { + // Compare string 1 to string 2 the same way strcasecmp() does. + result = s1StrRef.compare_lower(s2StrRef); + } else { + // Compare string 1 to string 2 the same way strcmp() does. + result = s1StrRef.compare(s2StrRef); + } + + // Build the SVal of the comparison and bind the return value. + SVal resultVal = svalBuilder.makeIntVal(result, CE->getType()); + state = state->BindExpr(CE, resultVal); + } } - if (ignoreCase) { - // Compare string 1 to string 2 the same way strcasecmp() does. - result = s1StrRef.compare_lower(s2StrRef); - } else { - // Compare string 1 to string 2 the same way strcmp() does. - result = s1StrRef.compare(s2StrRef); + if (!canComputeResult) { + // Conjure a symbolic value. It's the best we can do. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SVal resultVal = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + state = state->BindExpr(CE, resultVal); } - - // Build the SVal of the comparison to bind the return value. - SValBuilder &svalBuilder = C.getSValBuilder(); - QualType intTy = svalBuilder.getContext().IntTy; - SVal resultVal = svalBuilder.makeIntVal(result, intTy); - // Bind the return value of the expression. - // Set the return value. - state = state->BindExpr(CE, resultVal); + // Record this as a possible path. C.addTransition(state); } @@ -1251,7 +1661,7 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp) .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove) .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) - //.Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) + .Cases("strncpy", "__strncpy_chk", &CStringChecker::evalStrncpy) .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) .Cases("strcat", "__strcat_chk", &CStringChecker::evalStrcat) .Cases("strncat", "__strncat_chk", &CStringChecker::evalStrncat) @@ -1268,6 +1678,10 @@ bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { if (!evalFunction) return false; + // Make sure each function sets its own description. + // (But don't bother in a release build.) + assert(!(CurrentFunctionDescription = NULL)); + // Check and evaluate the call. (this->*evalFunction)(C, CE); return true; @@ -1373,8 +1787,10 @@ void CStringChecker::checkLiveSymbols(const GRState *state, for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end(); I != E; ++I) { SVal Len = I.getData(); - if (SymbolRef Sym = Len.getAsSymbol()) - SR.markInUse(Sym); + + for (SVal::symbol_iterator si = Len.symbol_begin(), se = Len.symbol_end(); + si != se; ++si) + SR.markInUse(*si); } } diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index dfe0a0e6f50a..6c3dfacec331 100644 --- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -282,7 +282,7 @@ void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, static bool supportsNilWithFloatRet(const llvm::Triple &triple) { return triple.getVendor() == llvm::Triple::Apple && - (triple.getDarwinMajorNumber() >= 9 || + (!triple.isMacOSXVersionLT(10,5) || triple.getArch() == llvm::Triple::arm || triple.getArch() == llvm::Triple::thumb); } diff --git a/lib/StaticAnalyzer/Checkers/Checkers.td b/lib/StaticAnalyzer/Checkers/Checkers.td index 1a71fc4074ba..2c196b597b7d 100644 --- a/lib/StaticAnalyzer/Checkers/Checkers.td +++ b/lib/StaticAnalyzer/Checkers/Checkers.td @@ -168,10 +168,6 @@ def ReturnUndefChecker : Checker<"UndefReturn">, let ParentPackage = CplusplusExperimental in { -def CStringChecker : Checker<"CString">, - HelpText<"Check calls to functions in <string.h>">, - DescFile<"CStringChecker.cpp">; - def IteratorsChecker : Checker<"Iterators">, HelpText<"Check improper uses of STL vector iterators">, DescFile<"IteratorsChecker.cpp">; @@ -244,6 +240,10 @@ def ChrootChecker : Checker<"Chroot">, HelpText<"Check improper use of chroot">, DescFile<"ChrootChecker.cpp">; +def CStringChecker : Checker<"CString">, + HelpText<"Check calls to functions in <string.h>">, + DescFile<"CStringChecker.cpp">; + def MallocChecker : Checker<"Malloc">, HelpText<"Check for potential memory leaks, double free, and use-after-free problems">, DescFile<"MallocChecker.cpp">; diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp index bc1d823dde03..ec2a88a55681 100644 --- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -254,13 +254,13 @@ public: return; if (Expr* E = V->getInit()) { + while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E)) + E = exprClean->getSubExpr(); + // Don't warn on C++ objects (yet) until we can show that their // constructors/destructors don't have side effects. if (isa<CXXConstructExpr>(E)) return; - - if (isa<ExprWithCleanups>(E)) - return; // A dead initialization is a variable that is dead after it // is initialized. We don't flag warnings for those variables diff --git a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp index e4e5f54770b3..de6da4f8c377 100644 --- a/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IteratorsChecker.cpp @@ -237,8 +237,11 @@ const GRState *IteratorsChecker::invalidateIterators(const GRState *state, const GRState *IteratorsChecker::handleAssign(const GRState *state, const Expr *lexp, const Expr *rexp, const LocationContext *LC) const { // Skip the cast if present. - if (isa<ImplicitCastExpr>(lexp)) - lexp = dyn_cast<ImplicitCastExpr>(lexp)->getSubExpr(); + if (const MaterializeTemporaryExpr *M + = dyn_cast<MaterializeTemporaryExpr>(lexp)) + lexp = M->GetTemporaryExpr(); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(lexp)) + lexp = ICE->getSubExpr(); SVal sv = state->getSVal(lexp); const MemRegion *MR = sv.getAsRegion(); if (!MR) @@ -260,8 +263,11 @@ const GRState *IteratorsChecker::handleAssign(const GRState *state, const MemRegion *MR, const Expr *rexp, const LocationContext *LC) const { // Assume unknown until we find something definite. state = state->set<IteratorState>(MR, RefState::getUnknown()); - if (isa<ImplicitCastExpr>(rexp)) - rexp = dyn_cast<ImplicitCastExpr>(rexp)->getSubExpr(); + if (const MaterializeTemporaryExpr *M + = dyn_cast<MaterializeTemporaryExpr>(rexp)) + rexp = M->GetTemporaryExpr(); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(rexp)) + rexp = ICE->getSubExpr(); // Need to handle three cases: MemberCall, copy, copy with addition. if (const CallExpr *CE = dyn_cast<CallExpr>(rexp)) { // Handle MemberCall. @@ -347,8 +353,10 @@ const DeclRefExpr *IteratorsChecker::getDeclRefExpr(const Expr *E) const { E = CE->getArg(0); } } - if (isa<ImplicitCastExpr>(E)) - E = dyn_cast<ImplicitCastExpr>(E)->getSubExpr(); + if (const MaterializeTemporaryExpr *M = dyn_cast<MaterializeTemporaryExpr>(E)) + E = M->GetTemporaryExpr(); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) + E = ICE->getSubExpr(); // If it isn't one of our types, don't do anything. if (getTemplateKind(E->getType()) != VectorIteratorKind) return NULL; @@ -520,8 +528,11 @@ void IteratorsChecker::checkPreStmt(const DeclStmt *DS, if (const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(InitEx)) { if (CE->getNumArgs() == 1) { const Expr *E = CE->getArg(0); - if (isa<ImplicitCastExpr>(E)) - InitEx = dyn_cast<ImplicitCastExpr>(E)->getSubExpr(); + if (const MaterializeTemporaryExpr *M + = dyn_cast<MaterializeTemporaryExpr>(E)) + E = M->GetTemporaryExpr(); + if (const ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(E)) + InitEx = ICE->getSubExpr(); state = handleAssign(state, MR, InitEx, C.getPredecessor()->getLocationContext()); } diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index 12ce86613ddc..f8d076b54e3d 100644 --- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -31,22 +31,17 @@ using namespace ento; namespace { class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > { - enum SubChecks { - DispatchOnce = 0, - DispatchOnceF, - NumChecks - }; - - mutable BugType *BTypes[NumChecks]; + mutable llvm::OwningPtr<BugType> BT_dispatchOnce; public: - MacOSXAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } - ~MacOSXAPIChecker() { - for (unsigned i=0; i != NumChecks; ++i) - delete BTypes[i]; - } - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + + void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, + const IdentifierInfo *FI) const; + + typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &, + const CallExpr *, + const IdentifierInfo *) const; }; } //end anonymous namespace @@ -54,16 +49,8 @@ public: // dispatch_once and dispatch_once_f //===----------------------------------------------------------------------===// -static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, - BugType *&BT, const IdentifierInfo *FI) { - - if (!BT) { - llvm::SmallString<128> S; - llvm::raw_svector_ostream os(S); - os << "Improper use of '" << FI->getName() << '\''; - BT = new BugType(os.str(), "Mac OS X API"); - } - +void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, + const IdentifierInfo *FI) const { if (CE->getNumArgs() < 1) return; @@ -78,6 +65,10 @@ static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (!N) return; + if (!BT_dispatchOnce) + BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", + "Mac OS X API")); + llvm::SmallString<256> S; llvm::raw_svector_ostream os(S); os << "Call to '" << FI->getName() << "' uses"; @@ -90,7 +81,7 @@ static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; - EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + RangedBugReport *report = new RangedBugReport(*BT_dispatchOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); } @@ -99,47 +90,29 @@ static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, // Central dispatch function. //===----------------------------------------------------------------------===// -typedef void (*SubChecker)(CheckerContext &C, const CallExpr *CE, BugType *&BT, - const IdentifierInfo *FI); -namespace { - class SubCheck { - SubChecker SC; - BugType **BT; - public: - SubCheck(SubChecker sc, BugType *& bt) : SC(sc), BT(&bt) {} - SubCheck() : SC(NULL), BT(NULL) {} - - void run(CheckerContext &C, const CallExpr *CE, - const IdentifierInfo *FI) const { - if (SC) - SC(C, CE, *BT, FI); - } - }; -} // end anonymous namespace - void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { - // FIXME: Mostly copy and paste from UnixAPIChecker. Should refactor. + // FIXME: This sort of logic is common to several checkers, including + // UnixAPIChecker, PthreadLockChecker, and CStringChecker. Should refactor. const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); - const FunctionTextRegion *Fn = - dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); if (!Fn) return; - const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); + const IdentifierInfo *FI = Fn->getIdentifier(); if (!FI) return; - const SubCheck &SC = - llvm::StringSwitch<SubCheck>(FI->getName()) - .Case("dispatch_once", SubCheck(CheckDispatchOnce, BTypes[DispatchOnce])) - .Case("dispatch_once_f", SubCheck(CheckDispatchOnce, - BTypes[DispatchOnceF])) - .Default(SubCheck()); + SubChecker SC = + llvm::StringSwitch<SubChecker>(FI->getName()) + .Cases("dispatch_once", "dispatch_once_f", + &MacOSXAPIChecker::CheckDispatchOnce) + .Default(NULL); - SC.run(C, CE, FI); + if (SC) + (this->*SC)(C, CE, FI); } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp index 07de8706df24..73ce359edc3e 100644 --- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -121,6 +121,11 @@ void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, return; if (R->hasStackStorage()) { + // Automatic reference counting automatically copies blocks. + if (C.getASTContext().getLangOptions().ObjCAutoRefCount && + isa<BlockDataRegion>(R)) + return; + EmitStackError(C, R, RetE); return; } @@ -135,12 +140,13 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, // a memory region in the stack space. class CallBack : public StoreManager::BindingsHandler { private: + ExprEngine &Eng; const StackFrameContext *CurSFC; public: llvm::SmallVector<std::pair<const MemRegion*, const MemRegion*>, 10> V; - CallBack(const LocationContext *LCtx) - : CurSFC(LCtx->getCurrentStackFrame()) {} + CallBack(ExprEngine &Eng, const LocationContext *LCtx) + : Eng(Eng), CurSFC(LCtx->getCurrentStackFrame()) {} bool HandleBinding(StoreManager &SMgr, Store store, const MemRegion *region, SVal val) { @@ -151,7 +157,13 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, const MemRegion *vR = val.getAsRegion(); if (!vR) return true; - + + // Under automated retain release, it is okay to assign a block + // directly to a global variable. + if (Eng.getContext().getLangOptions().ObjCAutoRefCount && + isa<BlockDataRegion>(vR)) + return true; + if (const StackSpaceRegion *SSR = dyn_cast<StackSpaceRegion>(vR->getMemorySpace())) { // If the global variable holds a location in the current stack frame, @@ -164,7 +176,7 @@ void StackAddrEscapeChecker::checkEndPath(EndOfFunctionNodeBuilder &B, } }; - CallBack cb(B.getPredecessor()->getLocationContext()); + CallBack cb(Eng, B.getPredecessor()->getLocationContext()); state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); if (cb.V.empty()) diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index 48d7c367a96d..0ecc391f3e8f 100644 --- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -28,26 +28,18 @@ using llvm::Optional; namespace { class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { - enum SubChecks { - OpenFn = 0, - PthreadOnceFn = 1, - MallocZero = 2, - NumChecks - }; - - mutable BugType *BTypes[NumChecks]; - -public: + mutable llvm::OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; mutable Optional<uint64_t> Val_O_CREAT; public: - UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } - ~UnixAPIChecker() { - for (unsigned i=0; i != NumChecks; ++i) - delete BTypes[i]; - } - void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + + void CheckOpen(CheckerContext &C, const CallExpr *CE) const; + void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; + void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; + + typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, + const CallExpr *) const; }; } //end anonymous namespace @@ -55,23 +47,23 @@ public: // Utility functions. //===----------------------------------------------------------------------===// -static inline void LazyInitialize(BugType *&BT, const char *name) { +static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT, + const char *name) { if (BT) return; - BT = new BugType(name, "Unix API"); + BT.reset(new BugType(name, "Unix API")); } //===----------------------------------------------------------------------===// // "open" (man 2 open) //===----------------------------------------------------------------------===// -static void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, - const CallExpr *CE, BugType *&BT) { +void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { // The definition of O_CREAT is platform specific. We need a better way // of querying this information from the checking environment. - if (!UC.Val_O_CREAT.hasValue()) { + if (!Val_O_CREAT.hasValue()) { if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) - UC.Val_O_CREAT = 0x0200; + Val_O_CREAT = 0x0200; else { // FIXME: We need a more general way of getting the O_CREAT value. // We could possibly grovel through the preprocessor state, but @@ -80,8 +72,6 @@ static void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, } } - LazyInitialize(BT, "Improper use of 'open'"); - // Look at the 'oflags' argument for the O_CREAT flag. const GRState *state = C.getState(); @@ -101,7 +91,7 @@ static void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, } NonLoc oflags = cast<NonLoc>(V); NonLoc ocreateFlag = - cast<NonLoc>(C.getSValBuilder().makeIntVal(UC.Val_O_CREAT.getValue(), + cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType())); SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, oflags, ocreateFlag, @@ -124,8 +114,10 @@ static void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, if (!N) return; - EnhancedBugReport *report = - new EnhancedBugReport(*BT, + LazyInitialize(BT_open, "Improper use of 'open'"); + + RangedBugReport *report = + new RangedBugReport(*BT_open, "Call to 'open' requires a third argument when " "the 'O_CREAT' flag is set", N); report->addRange(oflagsEx->getSourceRange()); @@ -137,14 +129,12 @@ static void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC, // pthread_once //===----------------------------------------------------------------------===// -static void CheckPthreadOnce(CheckerContext &C, const UnixAPIChecker &, - const CallExpr *CE, BugType *&BT) { +void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, + const CallExpr *CE) const { // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. // They can possibly be refactored. - LazyInitialize(BT, "Improper use of 'pthread_once'"); - if (CE->getNumArgs() < 1) return; @@ -171,7 +161,9 @@ static void CheckPthreadOnce(CheckerContext &C, const UnixAPIChecker &, if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) os << " Perhaps you intended to declare the variable as 'static'?"; - EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); + + RangedBugReport *report = new RangedBugReport(*BT_pthreadOnce, os.str(), N); report->addRange(CE->getArg(0)->getSourceRange()); C.EmitReport(report); } @@ -182,8 +174,8 @@ static void CheckPthreadOnce(CheckerContext &C, const UnixAPIChecker &, // FIXME: Eventually this should be rolled into the MallocChecker, but this // check is more basic and is valuable for widespread use. -static void CheckMallocZero(CheckerContext &C, const UnixAPIChecker &UC, - const CallExpr *CE, BugType *&BT) { +void UnixAPIChecker::CheckMallocZero(CheckerContext &C, + const CallExpr *CE) const { // Sanity check that malloc takes one argument. if (CE->getNumArgs() != 1) @@ -208,11 +200,11 @@ static void CheckMallocZero(CheckerContext &C, const UnixAPIChecker &UC, // FIXME: Add reference to CERT advisory, and/or C99 standard in bug // output. - LazyInitialize(BT, "Undefined allocation of 0 bytes"); + LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes"); EnhancedBugReport *report = - new EnhancedBugReport(*BT, "Call to 'malloc' has an allocation size" - " of 0 bytes", N); + new EnhancedBugReport(*BT_mallocZero, "Call to 'malloc' has an allocation" + " size of 0 bytes", N); report->addRange(CE->getArg(0)->getSourceRange()); report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, CE->getArg(0)); @@ -230,51 +222,29 @@ static void CheckMallocZero(CheckerContext &C, const UnixAPIChecker &UC, // Central dispatch function. //===----------------------------------------------------------------------===// -typedef void (*SubChecker)(CheckerContext &C, const UnixAPIChecker &UC, - const CallExpr *CE, BugType *&BT); -namespace { - class SubCheck { - SubChecker SC; - const UnixAPIChecker *UC; - BugType **BT; - public: - SubCheck(SubChecker sc, const UnixAPIChecker *uc, BugType *& bt) - : SC(sc), UC(uc), BT(&bt) {} - SubCheck() : SC(NULL), UC(NULL), BT(NULL) {} - - void run(CheckerContext &C, const CallExpr *CE) const { - if (SC) - SC(C, *UC, CE, *BT); - } - }; -} // end anonymous namespace - void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { // Get the callee. All the functions we care about are C functions // with simple identifiers. const GRState *state = C.getState(); const Expr *Callee = CE->getCallee(); - const FunctionTextRegion *Fn = - dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion()); + const FunctionDecl *Fn = state->getSVal(Callee).getAsFunctionDecl(); if (!Fn) return; - const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); + const IdentifierInfo *FI = Fn->getIdentifier(); if (!FI) return; - const SubCheck &SC = - llvm::StringSwitch<SubCheck>(FI->getName()) - .Case("open", - SubCheck(CheckOpen, this, BTypes[OpenFn])) - .Case("pthread_once", - SubCheck(CheckPthreadOnce, this, BTypes[PthreadOnceFn])) - .Case("malloc", - SubCheck(CheckMallocZero, this, BTypes[MallocZero])) - .Default(SubCheck()); - - SC.run(C, CE); + SubChecker SC = + llvm::StringSwitch<SubChecker>(FI->getName()) + .Case("open", &UnixAPIChecker::CheckOpen) + .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) + .Case("malloc", &UnixAPIChecker::CheckMallocZero) + .Default(NULL); + + if (SC) + (this->*SC)(C, CE); } //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/CFRefCount.cpp b/lib/StaticAnalyzer/Core/CFRefCount.cpp index 0512e2f08d04..bf5302920819 100644 --- a/lib/StaticAnalyzer/Core/CFRefCount.cpp +++ b/lib/StaticAnalyzer/Core/CFRefCount.cpp @@ -126,6 +126,7 @@ public: /// ArgEffect is used to summarize a function/method call's effect on a /// particular argument. enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, + DecRefBridgedTransfered, DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, NewAutoreleasePool, SelfOwn, StopTracking }; @@ -148,7 +149,8 @@ namespace { class RetEffect { public: enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, - NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, + NotOwnedSymbol, GCNotOwnedSymbol, ARCNotOwnedSymbol, + ReceiverAlias, OwnedWhenTrackedReceiver }; enum ObjKind { CF, ObjC, AnyObj }; @@ -195,7 +197,9 @@ public: static RetEffect MakeGCNotOwned() { return RetEffect(GCNotOwnedSymbol, ObjC); } - + static RetEffect MakeARCNotOwned() { + return RetEffect(ARCNotOwnedSymbol, ObjC); + } static RetEffect MakeNoRet() { return RetEffect(NoRet); } @@ -636,6 +640,9 @@ class RetainSummaryManager { /// GCEnabled - Records whether or not the analyzed code runs in GC mode. const bool GCEnabled; + /// Records whether or not the analyzed code runs in ARC mode. + const bool ARCEnabled; + /// FuncSummaries - A map from FunctionDecls to summaries. FuncSummariesTy FuncSummaries; @@ -788,14 +795,20 @@ private: public: - RetainSummaryManager(ASTContext& ctx, bool gcenabled) + RetainSummaryManager(ASTContext& ctx, bool gcenabled, bool usesARC) : Ctx(ctx), CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), - GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), - ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwned(RetEffect::ObjC, true)), - ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() - : RetEffect::MakeOwnedWhenTrackedReceiver()), + GCEnabled(gcenabled), + ARCEnabled(usesARC), + AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), + ObjCAllocRetE(gcenabled + ? RetEffect::MakeGCNotOwned() + : (usesARC ? RetEffect::MakeARCNotOwned() + : RetEffect::MakeOwned(RetEffect::ObjC, true))), + ObjCInitRetE(gcenabled + ? RetEffect::MakeGCNotOwned() + : (usesARC ? RetEffect::MakeARCNotOwned() + : RetEffect::MakeOwnedWhenTrackedReceiver())), DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */, RetEffect::MakeNoRet() /* return effect */, MayEscape, /* default argument effect */ @@ -871,6 +884,10 @@ public: bool isGCEnabled() const { return GCEnabled; } + bool isARCEnabled() const { return ARCEnabled; } + + bool isARCorGCEnabled() const { return GCEnabled || ARCEnabled; } + RetainSummary *copySummary(RetainSummary *OldSumm) { RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate<RetainSummary>(); new (Summ) RetainSummary(*OldSumm); @@ -1118,8 +1135,7 @@ RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { RetainSummary* RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD, StringRef FName) { - if (FName.find("Create") != StringRef::npos || - FName.find("Copy") != StringRef::npos) + if (coreFoundation::followsCreateRule(FName)) return getCFSummaryCreateRule(FD); return getCFSummaryGetRule(FD); @@ -1189,7 +1205,8 @@ RetainSummaryManager::getInitMethodSummary(QualType RetTy) { assert(ScratchArgs.isEmpty()); // 'init' methods conceptually return a newly allocated object and claim // the receiver. - if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy)) + if (cocoa::isCocoaObjectRef(RetTy) || + coreFoundation::isCFObjectRef(RetTy)) return getPersistentSummary(ObjCInitRetE, DecRefMsg); return getDefaultSummary(); @@ -1332,15 +1349,15 @@ RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, if (cocoa::isCocoaObjectRef(RetTy)) { // EXPERIMENTAL: assume the Cocoa conventions for all objects returned // by instance methods. - RetEffect E = cocoa::followsFundamentalRule(S) + RetEffect E = cocoa::followsFundamentalRule(S, MD) ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); return getPersistentSummary(E, ReceiverEff, MayEscape); } // Look for methods that return an owned core foundation object. - if (cocoa::isCFObjectRef(RetTy)) { - RetEffect E = cocoa::followsFundamentalRule(S) + if (coreFoundation::isCFObjectRef(RetTy)) { + RetEffect E = cocoa::followsFundamentalRule(S, MD) ? RetEffect::MakeOwned(RetEffect::CF, true) : RetEffect::MakeNotOwned(RetEffect::CF); @@ -1411,7 +1428,7 @@ RetainSummaryManager::getInstanceMethodSummary(Selector S, assert(ScratchArgs.isEmpty()); // "initXXX": pass-through for receiver. - if (cocoa::deriveNamingConvention(S) == cocoa::InitRule) + if (cocoa::deriveNamingConvention(S, MD) == cocoa::InitRule) Summ = getInitMethodSummary(RetTy); else Summ = getCommonMethodSummary(MD, S, RetTy); @@ -1654,7 +1671,6 @@ public: const char* nl, const char* sep); }; -private: typedef llvm::DenseMap<const ExplodedNode*, const RetainSummary*> SummaryLogTy; @@ -1691,7 +1707,7 @@ private: public: CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) - : Summaries(Ctx, gcenabled), + : Summaries(Ctx, gcenabled, (bool)lopts.ObjCAutoRefCount), LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), deallocGC(0), deallocNotOwned(0), leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), @@ -1706,6 +1722,8 @@ public: } bool isGCEnabled() const { return Summaries.isGCEnabled(); } + bool isARCorGCEnabled() const { return Summaries.isARCorGCEnabled(); } + const LangOptions& getLangOptions() const { return LOpts; } const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { @@ -1907,7 +1925,7 @@ namespace { CFRefBug(tf, "Method should return an owned object") {} const char *getDescription() const { - return "Object with +0 retain counts returned to caller where a +1 " + return "Object with a +0 retain count returned to caller where a +1 " "(owning) retain count is expected"; } }; @@ -2094,7 +2112,7 @@ PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, if (static_cast<CFRefBug&>(getBugType()).getTF().isGCEnabled()) { assert(CurrV.getObjKind() == RetEffect::CF); - os << " " + os << ". " "Core Foundation objects are not automatically garbage collected."; } } @@ -2416,14 +2434,14 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, if (RV->getKind() == RefVal::ErrorLeakReturned) { // FIXME: Per comments in rdar://6320065, "create" only applies to CF - // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership + // objects. Only "copy", "alloc", "retain" and "new" transfer ownership // to the caller for NS objects. const Decl *D = &EndN->getCodeDecl(); if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { os << " is returned from a method whose name ('" << MD->getSelector().getAsString() << "') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'." - " This violates the naming convention rules " + " This violates the naming convention rules" " given in the Memory Management Guide for Cocoa"; } else { @@ -2431,7 +2449,7 @@ CFRefLeakReport::getEndPath(BugReporterContext& BRC, os << " is return from a function whose name ('" << FD->getNameAsString() << "') does not contain 'Copy' or 'Create'. This violates the naming" - " convention rules given the Memory Management Guide for Core " + " convention rules given the Memory Management Guide for Core" " Foundation"; } } @@ -2777,6 +2795,7 @@ void CFRefCount::evalSummary(ExplodedNodeSet& Dst, } case RetEffect::GCNotOwnedSymbol: + case RetEffect::ARCNotOwnedSymbol: case RetEffect::NotOwnedSymbol: { unsigned Count = Builder.getCurrentBlockCount(); SValBuilder &svalBuilder = Eng.getSValBuilder(); @@ -3103,8 +3122,8 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, // In GC mode [... release] and [... retain] do nothing. switch (E) { default: break; - case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; - case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; + case IncRefMsg: E = isARCorGCEnabled() ? DoNothing : IncRef; break; + case DecRefMsg: E = isARCorGCEnabled() ? DoNothing : DecRef; break; case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : NewAutoreleasePool; break; @@ -3118,9 +3137,13 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, } switch (E) { - default: - assert (false && "Unhandled CFRef transition."); - + case DecRefMsg: + case IncRefMsg: + case MakeCollectable: + assert(false && + "DecRefMsg/IncRefMsg/MakeCollectable already transformed"); + return state; + case Dealloc: // Any use of -dealloc in GC is *bad*. if (isGCEnabled()) { @@ -3193,6 +3216,7 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, V = V ^ RefVal::NotOwned; // Fall-through. case DecRef: + case DecRefBridgedTransfered: switch (V.getKind()) { default: // case 'RefVal::Released' handled above. @@ -3200,7 +3224,9 @@ const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, case RefVal::Owned: assert(V.getCount() > 0); - if (V.getCount() == 1) V = V ^ RefVal::Released; + if (V.getCount() == 1) + V = V ^ (E == DecRefBridgedTransfered ? + RefVal::NotOwned : RefVal::Released); V = V - 1; break; @@ -3280,15 +3306,10 @@ CFRefCount::HandleAutoreleaseCounts(const GRState * state, std::string sbuf; llvm::raw_string_ostream os(sbuf); - os << "Object over-autoreleased: object was sent -autorelease"; + os << "Object over-autoreleased: object was sent -autorelease "; if (V.getAutoreleaseCount() > 1) - os << V.getAutoreleaseCount() << " times"; - os << " but the object has "; - if (V.getCount() == 0) - os << "zero (locally visible)"; - else - os << "+" << V.getCount(); - os << " retain counts"; + os << V.getAutoreleaseCount() << " times "; + os << "but the object has a +" << V.getCount() << " retain count"; CFRefReport *report = new CFRefReport(*static_cast<CFRefBug*>(overAutorelease), @@ -3468,7 +3489,9 @@ void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, namespace { class RetainReleaseChecker - : public Checker< check::PostStmt<BlockExpr>, check::RegionChanges > { + : public Checker< check::PostStmt<BlockExpr>, + check::PostStmt<CastExpr>, + check::RegionChanges > { public: bool wantsRegionUpdate; @@ -3476,6 +3499,9 @@ public: void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; + + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; + const GRState *checkRegionChanges(const GRState *state, const StoreManager::InvalidatedSymbols *invalidated, const MemRegion * const *begin, @@ -3545,6 +3571,48 @@ void RetainReleaseChecker::checkPostStmt(const BlockExpr *BE, C.addTransition(state); } +void RetainReleaseChecker::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); + if (!BE) + return; + + ArgEffect AE = IncRef; + + switch (BE->getBridgeKind()) { + case clang::OBC_Bridge: + // Do nothing. + return; + case clang::OBC_BridgeRetained: + AE = IncRef; + break; + case clang::OBC_BridgeTransfer: + AE = DecRefBridgedTransfered; + break; + } + + const GRState *state = C.getState(); + SymbolRef Sym = state->getSVal(CE).getAsLocSymbol(); + if (!Sym) + return; + const RefVal* T = state->get<RefBindings>(Sym); + if (!T) + return; + + // This is gross. Once the checker and CFRefCount are unified, + // this will go away. + CFRefCount &cf = static_cast<CFRefCount&>(C.getEngine().getTF()); + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = cf.Update(state, Sym, *T, AE, hasErr); + + if (hasErr) { + + return; + } + + C.generateNode(state); +} + //===----------------------------------------------------------------------===// // Transfer function creation for external clients. //===----------------------------------------------------------------------===// diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp index 48f126bfd863..3961c7b95259 100644 --- a/lib/StaticAnalyzer/Core/Environment.cpp +++ b/lib/StaticAnalyzer/Core/Environment.cpp @@ -83,6 +83,9 @@ SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder, case Stmt::CXXBindTemporaryExprClass: E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); continue; + case Stmt::MaterializeTemporaryExprClass: + E = cast<MaterializeTemporaryExpr>(E)->GetTemporaryExpr(); + continue; // Handle all other Stmt* using a lookup. default: break; diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp index aed39eb0cec6..ffe5f0b6cdf2 100644 --- a/lib/StaticAnalyzer/Core/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -442,7 +442,8 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, } switch (S->getStmtClass()) { - // C++ stuff we don't support yet. + // C++ and ARC stuff we don't support yet. + case Expr::ObjCIndirectCopyRestoreExprClass: case Stmt::CXXBindTemporaryExprClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXDependentScopeMemberExprClass: @@ -478,6 +479,7 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, // We don't handle default arguments either yet, but we can fake it // for now by just skipping them. + case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::CXXDefaultArgExprClass: { Dst.Add(Pred); break; @@ -508,7 +510,10 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, break; case Stmt::GNUNullExprClass: { - MakeNode(Dst, S, Pred, GetState(Pred)->BindExpr(S, svalBuilder.makeNull())); + // GNU __null is a pointer-width integer, not an actual pointer. + const GRState *state = GetState(Pred); + state = state->BindExpr(S, svalBuilder.makeIntValWithPtrWidth(0, false)); + MakeNode(Dst, S, Pred, state); break; } @@ -520,14 +525,27 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, VisitObjCPropertyRefExpr(cast<ObjCPropertyRefExpr>(S), Pred, Dst); break; + case Stmt::ImplicitValueInitExprClass: { + const GRState *state = GetState(Pred); + QualType ty = cast<ImplicitValueInitExpr>(S)->getType(); + SVal val = svalBuilder.makeZeroVal(ty); + MakeNode(Dst, S, Pred, state->BindExpr(S, val)); + break; + } + + case Stmt::ExprWithCleanupsClass: { + Visit(cast<ExprWithCleanups>(S)->getSubExpr(), Pred, Dst); + break; + } + // Cases not handled yet; but will handle some day. case Stmt::DesignatedInitExprClass: case Stmt::ExtVectorElementExprClass: case Stmt::ImaginaryLiteralClass: - case Stmt::ImplicitValueInitExprClass: case Stmt::ObjCAtCatchStmtClass: case Stmt::ObjCAtFinallyStmtClass: case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: case Stmt::ObjCEncodeExprClass: case Stmt::ObjCIsaExprClass: case Stmt::ObjCProtocolExprClass: @@ -548,7 +566,6 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::IntegerLiteralClass: case Stmt::CharacterLiteralClass: case Stmt::CXXBoolLiteralExprClass: - case Stmt::ExprWithCleanupsClass: case Stmt::FloatingLiteralClass: case Stmt::SizeOfPackExprClass: case Stmt::CXXNullPtrLiteralExprClass: @@ -668,12 +685,35 @@ void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, case Stmt::CXXDynamicCastExprClass: case Stmt::CXXReinterpretCastExprClass: case Stmt::CXXConstCastExprClass: - case Stmt::CXXFunctionalCastExprClass: { + case Stmt::CXXFunctionalCastExprClass: + case Stmt::ObjCBridgedCastExprClass: { const CastExpr* C = cast<CastExpr>(S); - VisitCast(C, C->getSubExpr(), Pred, Dst); + // Handle the previsit checks. + ExplodedNodeSet dstPrevisit; + getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, C, *this); + + // Handle the expression itself. + ExplodedNodeSet dstExpr; + for (ExplodedNodeSet::iterator i = dstPrevisit.begin(), + e = dstPrevisit.end(); i != e ; ++i) { + VisitCast(C, C->getSubExpr(), *i, dstExpr); + } + + // Handle the postvisit checks. + getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); break; } + case Expr::MaterializeTemporaryExprClass: { + const MaterializeTemporaryExpr *Materialize + = cast<MaterializeTemporaryExpr>(S); + if (!Materialize->getType()->isRecordType()) + CreateCXXTemporaryObject(Materialize->GetTemporaryExpr(), Pred, Dst); + else + Visit(Materialize->GetTemporaryExpr(), Pred, Dst); + break; + } + case Stmt::InitListExprClass: VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); break; @@ -2148,10 +2188,19 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, Pred = *I; switch (CastE->getCastKind()) { + case CK_LValueToRValue: + assert(false && "LValueToRValue casts handled earlier."); + case CK_GetObjCProperty: + assert(false && "GetObjCProperty casts handled earlier."); case CK_ToVoid: Dst.Add(Pred); continue; - case CK_LValueToRValue: + // The analyzer doesn't do anything special with these casts, + // since it understands retain/release semantics already. + case CK_ObjCProduceObject: + case CK_ObjCConsumeObject: + case CK_ObjCReclaimReturnedObject: // Fall-through. + // True no-ops. case CK_NoOp: case CK_FunctionToPointerDecay: { // Copy the SVal of Ex to CastE. @@ -2161,7 +2210,6 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, MakeNode(Dst, CastE, Pred, state); continue; } - case CK_GetObjCProperty: case CK_Dependent: case CK_ArrayToPointerDecay: case CK_BitCast: @@ -2273,17 +2321,9 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, // time a function is called those values may not be current. ExplodedNodeSet Tmp; - if (InitEx) { - if (VD->getType()->isReferenceType() && !InitEx->isLValue()) { - // If the initializer is C++ record type, it should already has a - // temp object. - if (!InitEx->getType()->isRecordType()) - CreateCXXTemporaryObject(InitEx, Pred, Tmp); - else - Tmp.Add(Pred); - } else - Visit(InitEx, Pred, Tmp); - } else + if (InitEx) + Visit(InitEx, Pred, Tmp); + else Tmp.Add(Pred); ExplodedNodeSet Tmp2; diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp index d0d8f601f071..23dd6416a8f1 100644 --- a/lib/StaticAnalyzer/Core/RegionStore.cpp +++ b/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -1377,7 +1377,12 @@ StoreRef RegionStoreManager::setImplicitDefaultValue(Store store, V = svalBuilder.makeZeroVal(Ctx.IntTy); } else { - return StoreRef(store, *this); + // We can't represent values of this type, but we still need to set a value + // to record that the region has been initialized. + // If this assertion ever fires, a new case should be added above -- we + // should know how to default-initialize any value we can symbolicate. + assert(!SymbolManager::canSymbolicate(T) && "This type is representable"); + V = UnknownVal(); } return StoreRef(addBinding(B, R, BindingKey::Default, diff --git a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp index 197442b1574f..80c18a335fde 100644 --- a/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -415,6 +415,24 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, case nonloc::ConcreteIntKind: { const nonloc::ConcreteInt& lhsInt = cast<nonloc::ConcreteInt>(lhs); + // Is the RHS a symbol we can simplify? + // FIXME: This was mostly copy/pasted from the LHS-is-a-symbol case. + if (const nonloc::SymbolVal *srhs = dyn_cast<nonloc::SymbolVal>(&rhs)) { + SymbolRef RSym = srhs->getSymbol(); + if (RSym->getType(Context)->isIntegerType()) { + if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { + // The symbol evaluates to a constant. + const llvm::APSInt *rhs_I; + if (BinaryOperator::isRelationalOp(op)) + rhs_I = &BasicVals.Convert(lhsInt.getValue(), *Constant); + else + rhs_I = &BasicVals.Convert(resultTy, *Constant); + + rhs = nonloc::ConcreteInt(*rhs_I); + } + } + } + if (isa<nonloc::ConcreteInt>(rhs)) { return lhsInt.evalBinOp(*this, op, cast<nonloc::ConcreteInt>(rhs)); } else { @@ -461,13 +479,22 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, case nonloc::SymbolValKind: { nonloc::SymbolVal *slhs = cast<nonloc::SymbolVal>(&lhs); SymbolRef Sym = slhs->getSymbol(); + QualType lhsType = Sym->getType(Context); + + // The conversion type is usually the result type, but not in the case + // of relational expressions. + QualType conversionType = resultTy; + if (BinaryOperator::isRelationalOp(op)) + conversionType = lhsType; + // Does the symbol simplify to a constant? If so, "fold" the constant // by setting 'lhs' to a ConcreteInt and try again. - if (Sym->getType(Context)->isIntegerType()) + if (lhsType->isIntegerType()) if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { // The symbol evaluates to a constant. If necessary, promote the // folded constant (LHS) to the result type. - const llvm::APSInt &lhs_I = BasicVals.Convert(resultTy, *Constant); + const llvm::APSInt &lhs_I = BasicVals.Convert(conversionType, + *Constant); lhs = nonloc::ConcreteInt(lhs_I); // Also promote the RHS (if necessary). @@ -479,7 +506,7 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, // Other operators: do an implicit conversion. This shouldn't be // necessary once we support truncation/extension of symbolic values. if (nonloc::ConcreteInt *rhs_I = dyn_cast<nonloc::ConcreteInt>(&rhs)){ - rhs = nonloc::ConcreteInt(BasicVals.Convert(resultTy, + rhs = nonloc::ConcreteInt(BasicVals.Convert(conversionType, rhs_I->getValue())); } @@ -492,7 +519,8 @@ SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, if (RSym->getType(Context)->isIntegerType()) { if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { // The symbol evaluates to a constant. - const llvm::APSInt &rhs_I = BasicVals.Convert(resultTy, *Constant); + const llvm::APSInt &rhs_I = BasicVals.Convert(conversionType, + *Constant); rhs = nonloc::ConcreteInt(rhs_I); } } diff --git a/runtime/compiler-rt/Makefile b/runtime/compiler-rt/Makefile index dcdd4f5ec03f..0d770db17c47 100644 --- a/runtime/compiler-rt/Makefile +++ b/runtime/compiler-rt/Makefile @@ -42,7 +42,7 @@ ifeq ($(shell test -d $(COMPILERRT_SRC_ROOT) && echo OK),OK) RuntimeDirs := ifeq ($(OS),Darwin) RuntimeDirs += darwin -RuntimeLibrary.darwin.Configs = eprintf 10.4 ios cc_kext +RuntimeLibrary.darwin.Configs = eprintf 10.4 osx ios cc_kext # On Darwin, fake Clang into using the iOS assembler (since compiler-rt wants to # build ARM bits). diff --git a/test/ARCMT/Common.h b/test/ARCMT/Common.h new file mode 100644 index 000000000000..c57f3e75e49e --- /dev/null +++ b/test/ARCMT/Common.h @@ -0,0 +1,55 @@ +#if __has_feature(objc_arr) +#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode"))) +#else +#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE +#endif + +typedef int BOOL; +typedef unsigned NSUInteger; +typedef int int32_t; +typedef unsigned char uint8_t; +typedef int32_t UChar32; +typedef unsigned char UChar; + +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +@end + +@interface NSObject <NSObject> {} +- (id)init; + ++ (id)new; ++ (id)alloc; +- (void)dealloc; + +- (void)finalize; + +- (id)copy; +- (id)mutableCopy; +@end + +NS_AUTOMATED_REFCOUNT_UNAVAILABLE +@interface NSAutoreleasePool : NSObject { +@private + void *_token; + void *_reserved3; + void *_reserved2; + void *_reserved; +} + ++ (void)addObject:(id)anObject; + +- (void)addObject:(id)anObject; + +- (void)drain; + +@end + +typedef const void* objc_objectptr_t; +extern __attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer); +extern __attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer); +extern objc_objectptr_t objc_unretainedPointer(id object); diff --git a/test/ARCMT/Inputs/test.h b/test/ARCMT/Inputs/test.h new file mode 100644 index 000000000000..756295f27e69 --- /dev/null +++ b/test/ARCMT/Inputs/test.h @@ -0,0 +1,15 @@ +@protocol NSObject +- (oneway void)release; +@end + +#ifdef PART1 +static inline void part1(id p) { + [p release]; +} +#endif + +#ifdef PART2 +static inline void part2(id p) { + [p release]; +} +#endif diff --git a/test/ARCMT/Inputs/test.h.result b/test/ARCMT/Inputs/test.h.result new file mode 100644 index 000000000000..0638a3378c1c --- /dev/null +++ b/test/ARCMT/Inputs/test.h.result @@ -0,0 +1,13 @@ +@protocol NSObject +- (oneway void)release; +@end + +#ifdef PART1 +static inline void part1(id p) { +} +#endif + +#ifdef PART2 +static inline void part2(id p) { +} +#endif diff --git a/test/ARCMT/Inputs/test1.m.in b/test/ARCMT/Inputs/test1.m.in new file mode 100644 index 000000000000..8416a8896569 --- /dev/null +++ b/test/ARCMT/Inputs/test1.m.in @@ -0,0 +1,6 @@ +#define PART1 +#include "test.h" + +void test1(id p) { + [p release]; +} diff --git a/test/ARCMT/Inputs/test1.m.in.result b/test/ARCMT/Inputs/test1.m.in.result new file mode 100644 index 000000000000..f351fe6c8355 --- /dev/null +++ b/test/ARCMT/Inputs/test1.m.in.result @@ -0,0 +1,5 @@ +#define PART1 +#include "test.h" + +void test1(id p) { +} diff --git a/test/ARCMT/Inputs/test2.m.in b/test/ARCMT/Inputs/test2.m.in new file mode 100644 index 000000000000..99f87b072171 --- /dev/null +++ b/test/ARCMT/Inputs/test2.m.in @@ -0,0 +1,6 @@ +#define PART2 +#include "test.h" + +void test2(id p) { + [p release]; +} diff --git a/test/ARCMT/Inputs/test2.m.in.result b/test/ARCMT/Inputs/test2.m.in.result new file mode 100644 index 000000000000..f8e918ce2598 --- /dev/null +++ b/test/ARCMT/Inputs/test2.m.in.result @@ -0,0 +1,5 @@ +#define PART2 +#include "test.h" + +void test2(id p) { +} diff --git a/test/ARCMT/assign-prop-no-arc-runtime.m b/test/ARCMT/assign-prop-no-arc-runtime.m new file mode 100644 index 000000000000..0baa1d28ff56 --- /dev/null +++ b/test/ARCMT/assign-prop-no-arc-runtime.m @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fobjc-nonfragile-abi -fsyntax-only %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface Foo : NSObject { + NSObject *x; +} +@property (readonly,assign) id x; +@end + +@implementation Foo +@synthesize x; +@end diff --git a/test/ARCMT/assign-prop-no-arc-runtime.m.result b/test/ARCMT/assign-prop-no-arc-runtime.m.result new file mode 100644 index 000000000000..49e91d83cfec --- /dev/null +++ b/test/ARCMT/assign-prop-no-arc-runtime.m.result @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-macosx10.6 -fobjc-nonfragile-abi -fsyntax-only %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface Foo : NSObject { + NSObject *__unsafe_unretained x; +} +@property (readonly,unsafe_unretained) id x; +@end + +@implementation Foo +@synthesize x; +@end diff --git a/test/ARCMT/assign-prop-with-arc-runtime.m b/test/ARCMT/assign-prop-with-arc-runtime.m new file mode 100644 index 000000000000..4e4ae774a25c --- /dev/null +++ b/test/ARCMT/assign-prop-with-arc-runtime.m @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fobjc-nonfragile-abi -fsyntax-only %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +__attribute__((objc_arc_weak_reference_unavailable)) +@interface WeakOptOut +@end + +@class _NSCachedAttributedString; +typedef _NSCachedAttributedString *BadClassForWeak; + +@class Forw; + +@interface Foo : NSObject { + Foo *x, *w, *q1, *q2; + WeakOptOut *oo; + BadClassForWeak bcw; + id not_safe1; + NSObject *not_safe2; + Forw *not_safe3; +} +@property (readonly,assign) Foo *x; +@property (assign) Foo *w; +@property Foo *q1, *q2; +@property (assign) WeakOptOut *oo; +@property (assign) BadClassForWeak bcw; +@property (assign) id not_safe1; +@property () NSObject *not_safe2; +@property Forw *not_safe3; + +@property (assign) Foo *no_user_ivar1; +@property (readonly) Foo *no_user_ivar2; +@end + +@implementation Foo +@synthesize x,w,q1,q2,oo,bcw,not_safe1,not_safe2,not_safe3; +@synthesize no_user_ivar1, no_user_ivar2; +@end diff --git a/test/ARCMT/assign-prop-with-arc-runtime.m.result b/test/ARCMT/assign-prop-with-arc-runtime.m.result new file mode 100644 index 000000000000..eb34c168e406 --- /dev/null +++ b/test/ARCMT/assign-prop-with-arc-runtime.m.result @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fobjc-nonfragile-abi -fsyntax-only %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +__attribute__((objc_arc_weak_reference_unavailable)) +@interface WeakOptOut +@end + +@class _NSCachedAttributedString; +typedef _NSCachedAttributedString *BadClassForWeak; + +@class Forw; + +@interface Foo : NSObject { + Foo *__weak x, *__weak w, *__weak q1, *__weak q2; + WeakOptOut *__unsafe_unretained oo; + BadClassForWeak __unsafe_unretained bcw; + id __unsafe_unretained not_safe1; + NSObject *__unsafe_unretained not_safe2; + Forw *__unsafe_unretained not_safe3; +} +@property (readonly,weak) Foo *x; +@property (weak) Foo *w; +@property (weak) Foo *q1, *q2; +@property (unsafe_unretained) WeakOptOut *oo; +@property (unsafe_unretained) BadClassForWeak bcw; +@property (unsafe_unretained) id not_safe1; +@property (unsafe_unretained) NSObject *not_safe2; +@property (unsafe_unretained) Forw *not_safe3; + +@property (weak) Foo *no_user_ivar1; +@property (weak, readonly) Foo *no_user_ivar2; +@end + +@implementation Foo +@synthesize x,w,q1,q2,oo,bcw,not_safe1,not_safe2,not_safe3; +@synthesize no_user_ivar1, no_user_ivar2; +@end diff --git a/test/ARCMT/atautorelease-2.m b/test/ARCMT/atautorelease-2.m new file mode 100644 index 000000000000..5d2977227781 --- /dev/null +++ b/test/ARCMT/atautorelease-2.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface NSAutoreleasePool +- drain; ++new; ++alloc; +-init; +-autorelease; +-release; +@end + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init]; + + while (argc) { + [chunkPool release]; + return 0; + } + + [chunkPool drain]; + [pool drain]; + + return 0; +} diff --git a/test/ARCMT/atautorelease-2.m.result b/test/ARCMT/atautorelease-2.m.result new file mode 100644 index 000000000000..7bb164723e19 --- /dev/null +++ b/test/ARCMT/atautorelease-2.m.result @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface NSAutoreleasePool +- drain; ++new; ++alloc; +-init; +-autorelease; +-release; +@end + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + @autoreleasepool { + @autoreleasepool { + + while (argc) { + return 0; + } + + } + } + + return 0; +} diff --git a/test/ARCMT/atautorelease-3.m b/test/ARCMT/atautorelease-3.m new file mode 100644 index 000000000000..5d71c33b6bc8 --- /dev/null +++ b/test/ARCMT/atautorelease-3.m @@ -0,0 +1,40 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface NSAutoreleasePool +- drain; ++new; ++alloc; +-init; +-autorelease; +- release; +@end + +void NSLog(id, ...); + +void test1(int x) { + // All this stuff get removed since nothing is happening inside. + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init]; + while (x) { + chunkPool = [[NSAutoreleasePool alloc] init]; + [chunkPool release]; + } + + [chunkPool drain]; + [pool drain]; +} + +void test2(int x) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init]; + while (x) { + chunkPool = [[NSAutoreleasePool alloc] init]; + ++x; + [chunkPool release]; + } + + [chunkPool drain]; + [pool drain]; +} diff --git a/test/ARCMT/atautorelease-3.m.result b/test/ARCMT/atautorelease-3.m.result new file mode 100644 index 000000000000..682118e82f28 --- /dev/null +++ b/test/ARCMT/atautorelease-3.m.result @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface NSAutoreleasePool +- drain; ++new; ++alloc; +-init; +-autorelease; +- release; +@end + +void NSLog(id, ...); + +void test1(int x) { + // All this stuff get removed since nothing is happening inside. +} + +void test2(int x) { + @autoreleasepool { + @autoreleasepool { + while (x) { + @autoreleasepool { + ++x; + } + } + + } + } +} diff --git a/test/ARCMT/atautorelease-check.m b/test/ARCMT/atautorelease-check.m new file mode 100644 index 000000000000..6528f3e62d1e --- /dev/null +++ b/test/ARCMT/atautorelease-check.m @@ -0,0 +1,144 @@ +// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi %s + +#if __has_feature(objc_arr) +#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE __attribute__((unavailable("not available in automatic reference counting mode"))) +#else +#define NS_AUTOMATED_REFCOUNT_UNAVAILABLE +#endif + +typedef struct _NSZone NSZone; +typedef int BOOL; +typedef unsigned NSUInteger; + +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (NSUInteger)retainCount NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (oneway void)release NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +- (id)autorelease NS_AUTOMATED_REFCOUNT_UNAVAILABLE; + +- (NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +@end + +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end + +@protocol NSMutableCopying +- (id)mutableCopyWithZone:(NSZone *)zone; +@end + +@interface NSObject <NSObject> {} +- (id)init; + ++ (id)new; ++ (id)allocWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; ++ (id)alloc; +- (void)dealloc; + +- (void)finalize; + +- (id)copy; +- (id)mutableCopy; + ++ (id)copyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; ++ (id)mutableCopyWithZone:(NSZone *)zone NS_AUTOMATED_REFCOUNT_UNAVAILABLE; +@end + +extern void NSRecycleZone(NSZone *zone); + +NS_AUTOMATED_REFCOUNT_UNAVAILABLE +@interface NSAutoreleasePool : NSObject { // expected-note 13 {{marked unavailable here}} +@private + void *_token; + void *_reserved3; + void *_reserved2; + void *_reserved; +} + ++ (void)addObject:(id)anObject; + +- (void)addObject:(id)anObject; + +- (void)drain; + +@end + + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + NSAutoreleasePool *chunkPool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} + + while (argc) { + [chunkPool release]; + // the following pool was not released in this scope, don't touch it. + chunkPool = [[NSAutoreleasePool alloc] init]; // expected-error {{'NSAutoreleasePool' is unavailable}} + } + + [chunkPool drain]; + [pool drain]; + + return 0; +} + +void f(void) { + NSAutoreleasePool * pool; // expected-error {{'NSAutoreleasePool' is unavailable}} + + for (int i=0; i != 10; ++i) { + id x = pool; // We won't touch a NSAutoreleasePool if we can't safely + // remove all the references to it. + } + + pool = [[NSAutoreleasePool alloc] init]; // expected-error {{'NSAutoreleasePool' is unavailable}} + NSLog(@"%s", "YES"); + [pool release]; +} + +void f2(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \ + // expected-note {{scope begins here}} + + // 'x' is declared inside the "pool scope" but used outside it, if we create + // a @autorelease scope it will be undefined outside it so don't touch the pool. + int x = 0; // expected-note {{declared here}} + + [pool release]; // expected-note {{scope ends here}} + + ++x; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}} +} + +void f3(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \ + // expected-note {{scope begins here}} + + struct S { int x; }; // expected-note {{declared here}} + + [pool release]; // expected-note {{scope ends here}} + + struct S *var; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}} + var->x = 0; +} + +void f4(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \ + // expected-note {{scope begins here}} + + enum { Bar }; // expected-note {{declared here}} + + [pool release]; // expected-note {{scope ends here}} + + int x = Bar; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}} +} + +void f5(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // expected-error 2 {{'NSAutoreleasePool' is unavailable}} \ + // expected-note {{scope begins here}} + + typedef int Bar; // expected-note {{declared here}} + + [pool release]; // expected-note {{scope ends here}} + + Bar x; // expected-error {{a name is referenced outside the NSAutoreleasePool scope that it was declared in}} +} diff --git a/test/ARCMT/atautorelease.m b/test/ARCMT/atautorelease.m new file mode 100644 index 000000000000..bdf77197a8a5 --- /dev/null +++ b/test/ARCMT/atautorelease.m @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + if (argc) { + NSAutoreleasePool * pool = [NSAutoreleasePool new]; + NSLog(@"%s", "YES"); + [pool drain]; + } + [pool drain]; + + NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init]; + NSLog(@"%s", "YES"); + [pool1 release]; + + return 0; +} + +void f(void) { + NSAutoreleasePool *pool1; + + pool1 = [NSAutoreleasePool new]; + int x = 4; + + NSAutoreleasePool *pool2 = [[NSAutoreleasePool alloc] init]; + ++x; + [pool2 drain]; + + [pool1 release]; +} + +int UIApplicationMain(int argc, char *argv[]); + +int main2(int argc, char *argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + int result = UIApplicationMain(argc, argv); + [pool release]; + return result; +} diff --git a/test/ARCMT/atautorelease.m.result b/test/ARCMT/atautorelease.m.result new file mode 100644 index 000000000000..ccce1bd87570 --- /dev/null +++ b/test/ARCMT/atautorelease.m.result @@ -0,0 +1,46 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + + @autoreleasepool { + + if (argc) { + @autoreleasepool { + NSLog(@"%s", "YES"); + } + } + } + + @autoreleasepool { + NSLog(@"%s", "YES"); + } + + return 0; +} + +void f(void) { + + @autoreleasepool { + int x = 4; + + @autoreleasepool { + ++x; + } + + } +} + +int UIApplicationMain(int argc, char *argv[]); + +int main2(int argc, char *argv[]) { + @autoreleasepool { + int result = UIApplicationMain(argc, argv); + return result; + } +} diff --git a/test/ARCMT/autoreleases.m b/test/ARCMT/autoreleases.m new file mode 100644 index 000000000000..a12db8178f7a --- /dev/null +++ b/test/ARCMT/autoreleases.m @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +typedef unsigned char BOOL; + +@interface NSObject { + id isa; +} ++new; ++alloc; +-init; +-autorelease; +@end + +@interface NSAutoreleasePool : NSObject +- drain; +@end + +@interface A : NSObject { +@package + id object; +} +@end + +@interface B : NSObject +- (BOOL)containsSelf:(A*)a; +@end + +@implementation A +@end + +@implementation B +- (BOOL)containsSelf:(A*)a { + return a->object == self; +} +@end + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + A *a = [[A new] autorelease]; + B *b = [[B new] autorelease]; + NSLog(@"%s", [b containsSelf:a] ? "YES" : "NO"); + [pool drain]; + return 0; +} diff --git a/test/ARCMT/autoreleases.m.result b/test/ARCMT/autoreleases.m.result new file mode 100644 index 000000000000..796bfbb9d659 --- /dev/null +++ b/test/ARCMT/autoreleases.m.result @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +typedef unsigned char BOOL; + +@interface NSObject { + id isa; +} ++new; ++alloc; +-init; +-autorelease; +@end + +@interface NSAutoreleasePool : NSObject +- drain; +@end + +@interface A : NSObject { +@package + id object; +} +@end + +@interface B : NSObject +- (BOOL)containsSelf:(A*)a; +@end + +@implementation A +@end + +@implementation B +- (BOOL)containsSelf:(A*)a { + return a->object == self; +} +@end + +void NSLog(id, ...); + +int main (int argc, const char * argv[]) { + @autoreleasepool { + A *a = [A new]; + B *b = [B new]; + NSLog(@"%s", [b containsSelf:a] ? "YES" : "NO"); + } + return 0; +} diff --git a/test/ARCMT/checking.m b/test/ARCMT/checking.m new file mode 100644 index 000000000000..eea7a9f2547c --- /dev/null +++ b/test/ARCMT/checking.m @@ -0,0 +1,295 @@ +// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi %s + +#include "Common.h" + +typedef const struct __CFString * CFStringRef; +extern const CFStringRef kUTTypePlainText; +extern const CFStringRef kUTTypeRTF; +@class NSString; +@class A; + +struct UnsafeS { + A *__unsafe_unretained unsafeObj; +}; + +@interface A : NSObject +- (id)retain; +- (id)retainCount; +- (id)autorelease; +- (id)init; +- (oneway void)release; +- (void)dealloc; +-(void)test; +-(id)delegate; +@end + +@implementation A +-(void)test { + [super dealloc]; +} +-(void)dealloc { + [super dealloc]; +} + +- (id)retain { return self; } // expected-error {{ARC forbids implementation}} +- (id)retainCount { return self; } // expected-error {{ARC forbids implementation}} +- (id)autorelease { return self; } // expected-error {{ARC forbids implementation}} +- (oneway void)release { } // expected-error {{ARC forbids implementation}} + +-(id)delegate { return self; } +@end + +id global_foo; + +void test1(A *a, BOOL b, struct UnsafeS *unsafeS) { + [[a delegate] release]; // expected-error {{it is not safe to remove 'retain' message on the result of a 'delegate' message; the object that was passed to 'setDelegate:' may not be properly retained}} \ + // expected-error {{ARC forbids explicit message send}} + [a.delegate release]; // expected-error {{it is not safe to remove 'retain' message on the result of a 'delegate' message; the object that was passed to 'setDelegate:' may not be properly retained}} \ + // expected-error {{ARC forbids explicit message send}} + [unsafeS->unsafeObj retain]; // expected-error {{it is not safe to remove 'retain' message on an __unsafe_unretained type}} \ + // expected-error {{ARC forbids explicit message send}} + id foo = [unsafeS->unsafeObj retain]; // no warning. + [global_foo retain]; // expected-error {{it is not safe to remove 'retain' message on a global variable}} \ + // expected-error {{ARC forbids explicit message send}} + [global_foo release]; // expected-error {{it is not safe to remove 'release' message on a global variable}} \ + // expected-error {{ARC forbids explicit message send}} + [a dealloc]; + [a retain]; + [a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}} + [a release]; + [a autorelease]; // expected-error {{it is not safe to remove an unused 'autorelease' message; its receiver may be destroyed immediately}} \ + // expected-error {{ARC forbids explicit message send}} + + CFStringRef cfstr; + NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}} + str = (NSString *)kUTTypePlainText; + str = b ? kUTTypeRTF : kUTTypePlainText; + str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText); + str = (NSString *)a; // no change. + + SEL s = @selector(retain); // expected-error {{ARC forbids use of 'retain' in a @selector}} + s = @selector(release); // expected-error {{ARC forbids use of 'release' in a @selector}} + s = @selector(autorelease); // expected-error {{ARC forbids use of 'autorelease' in a @selector}} + s = @selector(dealloc); // expected-error {{ARC forbids use of 'dealloc' in a @selector}} + + static id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing ownership}} +} + +struct S { + A* a; // expected-error {{ARC forbids Objective-C objects in structs or unions}} +}; + +@interface B +-(id)alloc; +- (id)initWithInt: (int) i; +@end + +void rdar8861761() { + B *o1 = [[B alloc] initWithInt:0]; + B *o2 = [B alloc]; + [o2 initWithInt:0]; +} + +@interface Test13 +- (id) init0; +- (void) noninit; +@end +@implementation Test13 +- (id) init0 { + self = 0; +} +- (void) noninit { + self = 0; // expected-error {{cannot assign to 'self' outside of a method in the init family}} + + for (id x in collection) { // expected-error {{use of undeclared identifier 'collection'}} + x = 0; + } +} +@end + +void * cvt(id arg) +{ + void* voidp_val; + (void)(int*)arg; // expected-error {{disallowed}} + (void)(id)arg; + (void)(__autoreleasing id*)arg; // expected-error {{disallowed}} + (void)(id*)arg; // expected-error {{disallowed}} + + (void)(__autoreleasing id**)voidp_val; + (void)(void*)voidp_val; + (void)(void**)arg; // expected-error {{disallowed}} + cvt((void*)arg); // expected-error {{requires a bridged cast}} expected-error {{disallowed}} \ + // expected-note {{use __bridge}} expected-note {{use __bridge_retained}} + cvt(0); + (void)(__strong id**)(0); + return arg; // expected-error {{disallowed}} +} + + +void test12(id collection) { + for (id x in collection) { + x = 0; + } + + for (__strong id x in collection) { + x = 0; + } +} + +void test6(unsigned cond) { + // FIXME: Fix this automatically ? + switch (cond) { + case 0: + ; + id x; // expected-note {{jump bypasses initialization of retaining variable}} + + case 1: // expected-error {{switch case is in protected scope}} + break; + } +} + +@class Test8_incomplete; +@interface Test8_complete @end; +@interface Test8_super @end; +@interface Test8 : Test8_super +- (id) init00; +- (id) init01; // expected-note {{declaration in interface}} +- (id) init02; +- (id) init03; // covariance +- (id) init04; // covariance +- (id) init05; + +- (void) init10; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init11; +- (void) init12; +- (void) init13; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init14; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init15; + +// These should be invalid to actually call. +- (Test8_incomplete*) init20; +- (Test8_incomplete*) init21; // expected-note {{declaration in interface}} +- (Test8_incomplete*) init22; +- (Test8_incomplete*) init23; +- (Test8_incomplete*) init24; +- (Test8_incomplete*) init25; + +- (Test8_super*) init30; // id exception to covariance +- (Test8_super*) init31; // expected-note {{declaration in interface}} +- (Test8_super*) init32; +- (Test8_super*) init33; +- (Test8_super*) init34; // covariance +- (Test8_super*) init35; + +- (Test8*) init40; // id exception to covariance +- (Test8*) init41; // expected-note {{declaration in interface}} +- (Test8*) init42; +- (Test8*) init43; // this should be a warning, but that's a general language thing, not an ARC thing +- (Test8*) init44; +- (Test8*) init45; + +- (Test8_complete*) init50; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init51; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init52; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init53; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init54; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init55; // expected-error {{init methods must return a type related to the receiver type}} +@end +@implementation Test8 +- (id) init00 { return 0; } +- (id) init10 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (id) init20 { return 0; } +- (id) init30 { return 0; } +- (id) init40 { return 0; } +- (id) init50 { return 0; } + +- (void) init01 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} +- (void) init11 {} +- (void) init21 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} +- (void) init31 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} +- (void) init41 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} +- (void) init51 {} + +- (Test8_incomplete*) init02 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init12 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init22 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init32 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init42 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init52 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} + +- (Test8_super*) init03 { return 0; } +- (Test8_super*) init13 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (Test8_super*) init23 { return 0; } +- (Test8_super*) init33 { return 0; } +- (Test8_super*) init43 { return 0; } +- (Test8_super*) init53 { return 0; } + +- (Test8*) init04 { return 0; } +- (Test8*) init14 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (Test8*) init24 { return 0; } +- (Test8*) init34 { return 0; } +- (Test8*) init44 { return 0; } +- (Test8*) init54 { return 0; } + +- (Test8_complete*) init05 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init15 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init25 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init35 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init45 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init55 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +@end + +@class Test9_incomplete; +@interface Test9 +- (Test9_incomplete*) init1; // expected-error {{init methods must return a type related to the receiver type}} +- (Test9_incomplete*) init2; +@end +id test9(Test9 *v) { + return [v init1]; +} + +// rdar://9491791 +void rdar9491791(int p) { + switch (p) { + case 3:; + NSObject *o = [[NSObject alloc] init]; // expected-note {{jump bypasses initialization of retaining variable}} + [o release]; + break; + default: // expected-error {{switch case is in protected scope}} + break; + } +} + +#define RELEASE_MACRO(x) do { [x release]; } while(1) + +// rdar://9504750 +void rdar9504750(id p) { + RELEASE_MACRO(p); // expected-error {{ARC forbids explicit message send of 'release'}} +} + +// rdar://8939557 +@interface TestReadonlyProperty : NSObject +@property(assign,readonly) NSObject *value; +@end + +@implementation TestReadonlyProperty +@synthesize value; +- (void)viewDidLoad { + value = [NSObject new]; // expected-error {{assigning retained object}} +} +@end + +// rdar://9601437 +@interface I9601437 { + __unsafe_unretained id x; +} +-(void)Meth; +@end + +@implementation I9601437 +-(void)Meth { + self->x = [NSObject new]; // expected-error {{assigning retained object}} +} +@end diff --git a/test/ARCMT/cxx-checking.mm b/test/ARCMT/cxx-checking.mm new file mode 100644 index 000000000000..27e0ea3869b8 --- /dev/null +++ b/test/ARCMT/cxx-checking.mm @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fblocks -Warc-abi %s + +// Classes that have an Objective-C object pointer. +struct HasObjectMember0 { // expected-warning{{'HasObjectMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x; +}; + +struct HasObjectMember1 { // expected-warning{{'HasObjectMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x[3]; +}; + +struct HasObjectMember2 { // expected-warning{{'HasObjectMember2' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x[3][2]; +}; + +// Don't complain if the type has non-external linkage +namespace { + struct HasObjectMember3 { + id x[3][2]; + }; +} + +// Don't complain if the Objective-C pointer type was explicitly given +// no lifetime. +struct HasObjectMember3 { + __unsafe_unretained id x[3][2]; +}; + +struct HasBlockPointerMember0 { // expected-warning{{'HasBlockPointerMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + int (^bp)(int); +}; + +struct HasBlockPointerMember1 { // expected-warning{{'HasBlockPointerMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + int (^bp[2][3])(int); +}; + +struct NonPOD { + NonPOD(const NonPOD&); +}; + +struct HasObjectMemberAndNonPOD0 { // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + id x; + NonPOD np; +}; + +struct HasObjectMemberAndNonPOD1 { // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + id x[3]; +}; + +struct HasObjectMemberAndNonPOD2 { // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + id x[3][2]; +}; + +struct HasObjectMemberAndNonPOD3 { + HasObjectMemberAndNonPOD3 &operator=(const HasObjectMemberAndNonPOD3&); + ~HasObjectMemberAndNonPOD3(); + NonPOD np; + id x[3][2]; +}; + +struct HasBlockPointerMemberAndNonPOD0 { // expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ +// expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + int (^bp)(int); +}; + +struct HasBlockPointerMemberAndNonPOD1 { // expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ +// expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + int (^bp[2][3])(int); +}; + +int check_non_pod_objc_pointer0[__is_pod(id)? 1 : -1]; +int check_non_pod_objc_pointer1[__is_pod(__strong id)? -1 : 1]; +int check_non_pod_objc_pointer2[__is_pod(__unsafe_unretained id)? 1 : -1]; +int check_non_pod_objc_pointer3[__is_pod(id[2][3])? 1 : -1]; +int check_non_pod_objc_pointer4[__is_pod(__unsafe_unretained id[2][3])? 1 : -1]; +int check_non_pod_block0[__is_pod(int (^)(int))? 1 : -1]; +int check_non_pod_block1[__is_pod(int (^ __unsafe_unretained)(int))? 1 : -1]; + +struct FlexibleArrayMember0 { + int length; + id array[]; // expected-error{{flexible array member 'array' of non-POD element type 'id __strong[]'}} +}; + +struct FlexibleArrayMember1 { + int length; + __unsafe_unretained id array[]; +}; + +// It's okay to pass a retainable type through an ellipsis. +void variadic(...); +void test_variadic() { + variadic(1, 17, @"Foo"); +} + +// It's okay to create a VLA of retainable types. +void vla(int n) { + id vla[n]; +} diff --git a/test/ARCMT/cxx-rewrite.mm b/test/ARCMT/cxx-rewrite.mm new file mode 100644 index 000000000000..aba3f7568b03 --- /dev/null +++ b/test/ARCMT/cxx-rewrite.mm @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c++ %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c++ %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface NSString : NSObject ++(id)string; +@end + +struct foo { + NSString *s; + foo(NSString *s): s([s retain]){ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + [[[NSString string] retain] release]; + [pool drain]; + } + ~foo(){ [s release]; } +private: + foo(foo const &); + foo &operator=(foo const &); +}; + +int main(){ + NSAutoreleasePool *pool = [NSAutoreleasePool new]; + + foo f([[NSString string] autorelease]); + + [pool drain]; + return 0; +} diff --git a/test/ARCMT/cxx-rewrite.mm.result b/test/ARCMT/cxx-rewrite.mm.result new file mode 100644 index 000000000000..145ccd39e884 --- /dev/null +++ b/test/ARCMT/cxx-rewrite.mm.result @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c++ %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c++ %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface NSString : NSObject ++(id)string; +@end + +struct foo { + NSString *s; + foo(NSString *s): s(s){ + @autoreleasepool { + [NSString string]; + } + } + ~foo(){ s; } +private: + foo(foo const &); + foo &operator=(foo const &); +}; + +int main(){ + @autoreleasepool { + + foo f([NSString string]); + + } + return 0; +} diff --git a/test/ARCMT/dealloc.m b/test/ARCMT/dealloc.m new file mode 100644 index 000000000000..dac964403f27 --- /dev/null +++ b/test/ARCMT/dealloc.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface A +- (id)retain; +- (id)autorelease; +- (oneway void)release; +- (void)dealloc; +@end + +void test1(A *a) { + [a dealloc]; +} + +@interface Test2 : A +- (void) dealloc; +@end + +@implementation Test2 +- (void) dealloc { + [super dealloc]; +} +@end diff --git a/test/ARCMT/dealloc.m.result b/test/ARCMT/dealloc.m.result new file mode 100644 index 000000000000..24756d25484b --- /dev/null +++ b/test/ARCMT/dealloc.m.result @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface A +- (id)retain; +- (id)autorelease; +- (oneway void)release; +- (void)dealloc; +@end + +void test1(A *a) { +} + +@interface Test2 : A +- (void) dealloc; +@end + +@implementation Test2 +@end diff --git a/test/ARCMT/driver-migrate.m b/test/ARCMT/driver-migrate.m new file mode 100644 index 000000000000..e283857cb574 --- /dev/null +++ b/test/ARCMT/driver-migrate.m @@ -0,0 +1,3 @@ +// RUN: %clang -### -ccc-arcmt-migrate /foo/bar -fsyntax-only %s 2>&1 | FileCheck %s + +// CHECK: "-arcmt-migrate" "-arcmt-migrate-directory" "{{[^"]*}}/foo/bar" diff --git a/test/ARCMT/init.m b/test/ARCMT/init.m new file mode 100644 index 000000000000..f0d24f60e1e3 --- /dev/null +++ b/test/ARCMT/init.m @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface NSObject +-init; +@end + +@interface A : NSObject +-init; +-init2; +-foo; ++alloc; +@end + +@implementation A +-(id) init { + [self init]; + id a; + [a init]; + a = [[A alloc] init]; + + return self; +} + +-(id) init2 { + [super init]; + return self; +} + +-(id) foo { + [self init]; + [super init]; + + return self; +} +@end diff --git a/test/ARCMT/init.m.result b/test/ARCMT/init.m.result new file mode 100644 index 000000000000..4f3b4e724e2e --- /dev/null +++ b/test/ARCMT/init.m.result @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface NSObject +-init; +@end + +@interface A : NSObject +-init; +-init2; +-foo; ++alloc; +@end + +@implementation A +-(id) init { + self = [self init]; + id a; + [a init]; + a = [[A alloc] init]; + + return self; +} + +-(id) init2 { + self = [super init]; + return self; +} + +-(id) foo { + [self init]; + [super init]; + + return self; +} +@end diff --git a/test/ARCMT/migrate.m b/test/ARCMT/migrate.m new file mode 100644 index 000000000000..51029c5204f7 --- /dev/null +++ b/test/ARCMT/migrate.m @@ -0,0 +1,4 @@ +// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t %S/Inputs/test1.m.in -x objective-c -fobjc-nonfragile-abi +// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t %S/Inputs/test2.m.in -x objective-c -fobjc-nonfragile-abi +// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %S/Inputs/test1.m.in.result %S/Inputs/test2.m.in.result %S/Inputs/test.h.result +// RUN: rm -rf %t diff --git a/test/ARCMT/nonobjc-to-objc-cast-2.m b/test/ARCMT/nonobjc-to-objc-cast-2.m new file mode 100644 index 000000000000..46ef02400e8c --- /dev/null +++ b/test/ARCMT/nonobjc-to-objc-cast-2.m @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -arcmt-check -verify -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi %s + +typedef int BOOL; +typedef const struct __CFString * CFStringRef; + +@class NSString; + +void f(BOOL b) { + CFStringRef cfstr; + NSString *str = (NSString *)cfstr; // expected-error {{cast of C pointer type 'CFStringRef' (aka 'const struct __CFString *') to Objective-C pointer type 'NSString *' requires a bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFStringRef' (aka 'const struct __CFString *') into ARC}} + void *vp = str; // expected-error {{disallowed}} +} diff --git a/test/ARCMT/nonobjc-to-objc-cast.m b/test/ARCMT/nonobjc-to-objc-cast.m new file mode 100644 index 000000000000..4e1e293efbac --- /dev/null +++ b/test/ARCMT/nonobjc-to-objc-cast.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface NSString : NSObject +@end + +typedef const struct __CFString * CFStringRef; +extern const CFStringRef kUTTypePlainText; +extern const CFStringRef kUTTypeRTF; + +typedef const struct __CFAllocator * CFAllocatorRef; +typedef const struct __CFUUID * CFUUIDRef; + +extern const CFAllocatorRef kCFAllocatorDefault; + +extern CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid); + +void f(BOOL b, id p) { + NSString *str = (NSString *)kUTTypePlainText; + str = b ? kUTTypeRTF : kUTTypePlainText; + str = (NSString *)(b ? kUTTypeRTF : kUTTypePlainText); + str = (NSString *)p; // no change. + + CFUUIDRef _uuid; + NSString *_uuidString = (NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid); + _uuidString = [(NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid) autorelease]; +} + +@implementation NSString (StrExt) +- (NSString *)stringEscapedAsURI { + CFStringRef str = (CFStringRef)self; + CFStringRef str2 = self; + return self; +} +@end diff --git a/test/ARCMT/nonobjc-to-objc-cast.m.result b/test/ARCMT/nonobjc-to-objc-cast.m.result new file mode 100644 index 000000000000..53dde7553d6d --- /dev/null +++ b/test/ARCMT/nonobjc-to-objc-cast.m.result @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface NSString : NSObject +@end + +typedef const struct __CFString * CFStringRef; +extern const CFStringRef kUTTypePlainText; +extern const CFStringRef kUTTypeRTF; + +typedef const struct __CFAllocator * CFAllocatorRef; +typedef const struct __CFUUID * CFUUIDRef; + +extern const CFAllocatorRef kCFAllocatorDefault; + +extern CFStringRef CFUUIDCreateString(CFAllocatorRef alloc, CFUUIDRef uuid); + +void f(BOOL b, id p) { + NSString *str = (__bridge NSString *)kUTTypePlainText; + str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText); + str = (__bridge NSString *)(b ? kUTTypeRTF : kUTTypePlainText); + str = (NSString *)p; // no change. + + CFUUIDRef _uuid; + NSString *_uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid); + _uuidString = (__bridge_transfer NSString *)CFUUIDCreateString(kCFAllocatorDefault, _uuid); +} + +@implementation NSString (StrExt) +- (NSString *)stringEscapedAsURI { + CFStringRef str = (__bridge CFStringRef)self; + CFStringRef str2 = (__bridge CFStringRef)(self); + return self; +} +@end diff --git a/test/ARCMT/releases-driver.m b/test/ARCMT/releases-driver.m new file mode 100644 index 000000000000..a016b26f4482 --- /dev/null +++ b/test/ARCMT/releases-driver.m @@ -0,0 +1,68 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: cp %s %t +// RUN: %clang_cc1 -arcmt-modify -triple x86_64-apple-macosx10.6 -fobjc-nonfragile-abi -x objective-c %t +// RUN: diff %t %s.result +// RUN: rm %t + +typedef int BOOL; + +id IhaveSideEffect(); + +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain; +- (oneway void)release; +@end + +@interface NSObject <NSObject> {} +@end + +@interface Foo : NSObject { + id bar; +} +@property (retain) id bar; +-(void)test:(id)obj; +@end + +@implementation Foo + +@synthesize bar; + +-(void)test:(id)obj { + id x = self.bar; + [x retain]; + self.bar = obj; + // do stuff with x; + [x release]; + + [IhaveSideEffect() release]; + + [x release], x = 0; +} + +@end + +void func(Foo *p) { + [p release]; + (([p release])); +} + +@interface Baz { + id <NSObject> _foo; +} +@end + +@implementation Baz +- dealloc { + [_foo release]; + return 0; +} +@end + +#define RELEASE_MACRO(x) [x release] +#define RELEASE_MACRO2(x) RELEASE_MACRO(x) + +void test2(id p) { + RELEASE_MACRO(p); + RELEASE_MACRO2(p); +} diff --git a/test/ARCMT/releases-driver.m.result b/test/ARCMT/releases-driver.m.result new file mode 100644 index 000000000000..c5f527fec1c1 --- /dev/null +++ b/test/ARCMT/releases-driver.m.result @@ -0,0 +1,61 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: cp %s %t +// RUN: %clang_cc1 -arcmt-modify -triple x86_64-apple-macosx10.6 -fobjc-nonfragile-abi -x objective-c %t +// RUN: diff %t %s.result +// RUN: rm %t + +typedef int BOOL; + +id IhaveSideEffect(); + +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain; +- (oneway void)release; +@end + +@interface NSObject <NSObject> {} +@end + +@interface Foo : NSObject { + id bar; +} +@property (strong) id bar; +-(void)test:(id)obj; +@end + +@implementation Foo + +@synthesize bar; + +-(void)test:(id)obj { + id x = self.bar; + self.bar = obj; + // do stuff with x; + + IhaveSideEffect(); + + x = 0; +} + +@end + +void func(Foo *p) { +} + +@interface Baz { + id <NSObject> _foo; +} +@end + +@implementation Baz +- dealloc { + return 0; +} +@end + +#define RELEASE_MACRO(x) [x release] +#define RELEASE_MACRO2(x) RELEASE_MACRO(x) + +void test2(id p) { +} diff --git a/test/ARCMT/releases.m b/test/ARCMT/releases.m new file mode 100644 index 000000000000..d5f21dc4c2a7 --- /dev/null +++ b/test/ARCMT/releases.m @@ -0,0 +1,87 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-exceptions -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-exceptions -fblocks -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#define nil 0 + +typedef int BOOL; + +id IhaveSideEffect(); + +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain; +- (oneway void)release; +@end + +@interface NSObject <NSObject> {} +@end + +@interface Foo : NSObject { + id bar; +} +@property (retain) id bar; +-(void)test:(id)obj; +@end + +@implementation Foo + +@synthesize bar; + +-(void)test:(id)obj { + id x = self.bar; + [x retain]; + self.bar = obj; + // do stuff with x; + [x release]; + + [IhaveSideEffect() release]; + + [x release], x = 0; + + @try { + } @finally { + [x release]; + } +} + +@end + +void func(Foo *p) { + [p release]; + (([p release])); +} + +@interface Baz { + id <NSObject> _foo; +} +@end + +@implementation Baz +- dealloc { + [_foo release]; + return 0; +} +@end + +void block_test(Foo *p) { + id (^B)() = ^() { + if (p) { + id (^IB)() = ^() { + id bar = [p retain]; + [p release]; + return bar; + }; + IB(); + } + return [p retain]; + }; +} + +#define RELEASE_MACRO(x) [x release] +#define RELEASE_MACRO2(x) RELEASE_MACRO(x) + +void test2(id p) { + RELEASE_MACRO(p); + RELEASE_MACRO2(p); +} diff --git a/test/ARCMT/releases.m.result b/test/ARCMT/releases.m.result new file mode 100644 index 000000000000..1d36f3e6b7de --- /dev/null +++ b/test/ARCMT/releases.m.result @@ -0,0 +1,79 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-exceptions -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-exceptions -fblocks -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#define nil 0 + +typedef int BOOL; + +id IhaveSideEffect(); + +@protocol NSObject +- (BOOL)isEqual:(id)object; +- (id)retain; +- (oneway void)release; +@end + +@interface NSObject <NSObject> {} +@end + +@interface Foo : NSObject { + id bar; +} +@property (strong) id bar; +-(void)test:(id)obj; +@end + +@implementation Foo + +@synthesize bar; + +-(void)test:(id)obj { + id x = self.bar; + self.bar = obj; + // do stuff with x; + + IhaveSideEffect(); + + x = 0; + + @try { + } @finally { + x = nil; + } +} + +@end + +void func(Foo *p) { +} + +@interface Baz { + id <NSObject> _foo; +} +@end + +@implementation Baz +- dealloc { + return 0; +} +@end + +void block_test(Foo *p) { + id (^B)() = ^() { + if (p) { + id (^IB)() = ^() { + id bar = p; + return bar; + }; + IB(); + } + return p; + }; +} + +#define RELEASE_MACRO(x) [x release] +#define RELEASE_MACRO2(x) RELEASE_MACRO(x) + +void test2(id p) { +} diff --git a/test/ARCMT/remove-dealloc-method.m b/test/ARCMT/remove-dealloc-method.m new file mode 100644 index 000000000000..5c2d785ba716 --- /dev/null +++ b/test/ARCMT/remove-dealloc-method.m @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#define nil ((void*) 0) + +@interface Foo +@property (retain) id x; +@property (retain) id y; +@property (retain) id w; +@property (retain) id z; +@end + +@implementation Foo +@synthesize x; +@synthesize y; +@synthesize w; +@synthesize z; + +- (void) dealloc { + self.x = 0; + [self setY:nil]; + w = nil; + self.z = nil; +} +@end diff --git a/test/ARCMT/remove-dealloc-method.m.result b/test/ARCMT/remove-dealloc-method.m.result new file mode 100644 index 000000000000..ecb752cf2f88 --- /dev/null +++ b/test/ARCMT/remove-dealloc-method.m.result @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#define nil ((void*) 0) + +@interface Foo +@property (strong) id x; +@property (strong) id y; +@property (strong) id w; +@property (strong) id z; +@end + +@implementation Foo +@synthesize x; +@synthesize y; +@synthesize w; +@synthesize z; + +@end diff --git a/test/ARCMT/remove-dealloc-zerouts.m b/test/ARCMT/remove-dealloc-zerouts.m new file mode 100644 index 000000000000..3ba85f1edcab --- /dev/null +++ b/test/ARCMT/remove-dealloc-zerouts.m @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface Foo +@property (retain) id x; +@property (retain) id y; +@property (retain) id w; +@property (retain) id z; +@property (strong) id q; +@end + +@implementation Foo +@synthesize x; +@synthesize y; +@synthesize w; +@synthesize q; +@dynamic z; + +- (void) dealloc { + self.x = self.y = self.w = 0; + self.x = 0, w = 0, y = 0; + [self setY:0]; + w = 0; + q = 0; + self.z = 0; +} +@end + +@interface Bar +@property (retain) Foo *a; +- (void) setA:(Foo*) val; +- (id) a; +@end + +@implementation Bar +- (void) dealloc { + [self setA:0]; // This is user-defined setter overriding synthesize, don't touch it. + self.a.x = 0; // every dealloc must zero out its own ivar. This patter is not recognized. +} +@synthesize a; +- (void) setA:(Foo*) val { } +- (id) a {return 0;} +@end diff --git a/test/ARCMT/remove-dealloc-zerouts.m.result b/test/ARCMT/remove-dealloc-zerouts.m.result new file mode 100644 index 000000000000..dc6ffd311490 --- /dev/null +++ b/test/ARCMT/remove-dealloc-zerouts.m.result @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +@interface Foo +@property (strong) id x; +@property (strong) id y; +@property (strong) id w; +@property (strong) id z; +@property (strong) id q; +@end + +@implementation Foo +@synthesize x; +@synthesize y; +@synthesize w; +@synthesize q; +@dynamic z; + +- (void) dealloc { + self.z = 0; +} +@end + +@interface Bar +@property (strong) Foo *a; +- (void) setA:(Foo*) val; +- (id) a; +@end + +@implementation Bar +- (void) dealloc { + [self setA:0]; // This is user-defined setter overriding synthesize, don't touch it. + self.a.x = 0; // every dealloc must zero out its own ivar. This patter is not recognized. +} +@synthesize a; +- (void) setA:(Foo*) val { } +- (id) a {return 0;} +@end diff --git a/test/ARCMT/remove-statements.m b/test/ARCMT/remove-statements.m new file mode 100644 index 000000000000..7e102961263a --- /dev/null +++ b/test/ARCMT/remove-statements.m @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface myController : NSObject +-(id)test:(id)x; +@end + +#define MY_MACRO1(x) +#define MY_MACRO2(x) (void)x + +@implementation myController +-(id) test:(id) x { + [[x retain] release]; + return [[x retain] autorelease]; +} + +-(void)dealloc +{ + id array, array_already_empty; + for (id element in array_already_empty) { + } + + [array release]; + ; + + int b, b_array_already_empty; + if (b) + [array release]; + if (b_array_already_empty) ; + + if (b) { + [array release]; + } + if (b_array_already_empty) { + } + + if (b) + MY_MACRO1(array); + if (b) + MY_MACRO2(array); +} +@end diff --git a/test/ARCMT/remove-statements.m.result b/test/ARCMT/remove-statements.m.result new file mode 100644 index 000000000000..9a1153e287c2 --- /dev/null +++ b/test/ARCMT/remove-statements.m.result @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface myController : NSObject +-(id)test:(id)x; +@end + +#define MY_MACRO1(x) +#define MY_MACRO2(x) (void)x + +@implementation myController +-(id) test:(id) x { + return x; +} + +-(void)dealloc +{ + id array, array_already_empty; + for (id element in array_already_empty) { + } + + ; + + int b, b_array_already_empty; + if (b_array_already_empty) ; + + if (b_array_already_empty) { + } + + if (b) + MY_MACRO1(array); + if (b) + MY_MACRO2(array); +} +@end diff --git a/test/ARCMT/retains.m b/test/ARCMT/retains.m new file mode 100644 index 000000000000..fa90f218dd5d --- /dev/null +++ b/test/ARCMT/retains.m @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fblocks -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +id IhaveSideEffect(); + +@interface Foo : NSObject { + id bar; +} +@property (retain) id bar; +-(id)test:(id)obj; +-(id)something; +@end + +#define Something_Macro(key, comment) \ + [[Foo new] something] + +@implementation Foo + +@synthesize bar; + +-(id)something {} + +-(id)test:(id)obj { + id x = self.bar; + [x retain]; + self.bar = obj; + if (obj) + [obj retain]; + + [Something_Macro(@"foo", "@bar") retain]; + + [IhaveSideEffect() retain]; + + [[self something] retain]; + + [[self retain] something]; + + [[IhaveSideEffect() retain] release]; + [[x retain] release]; + // do stuff with x; + [x release]; + return [self retain]; +} + +- (id)test1 { + id x=0; + ([x retain]); + return ((([x retain]))); +} +@end + +id foo (Foo *p) { + p = [p retain]; + return ([p retain]); +} + +void block_tests(Foo *p) { + id (^B)() = ^() { + if (p) { + id (^IB)() = ^() { + id bar = [p retain]; + return bar; + }; + IB(); + } + return [p retain]; + }; +} diff --git a/test/ARCMT/retains.m.result b/test/ARCMT/retains.m.result new file mode 100644 index 000000000000..54df864e58cb --- /dev/null +++ b/test/ARCMT/retains.m.result @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fblocks -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +id IhaveSideEffect(); + +@interface Foo : NSObject { + id bar; +} +@property (strong) id bar; +-(id)test:(id)obj; +-(id)something; +@end + +#define Something_Macro(key, comment) \ + [[Foo new] something] + +@implementation Foo + +@synthesize bar; + +-(id)something {} + +-(id)test:(id)obj { + id x = self.bar; + self.bar = obj; + + Something_Macro(@"foo", "@bar"); + + IhaveSideEffect(); + + [self something]; + + [self something]; + + IhaveSideEffect(); + // do stuff with x; + return self; +} + +- (id)test1 { + id x=0; + return (((x))); +} +@end + +id foo (Foo *p) { + p = p; + return (p); +} + +void block_tests(Foo *p) { + id (^B)() = ^() { + if (p) { + id (^IB)() = ^() { + id bar = p; + return bar; + }; + IB(); + } + return p; + }; +} diff --git a/test/ARCMT/rewrite-block-var.m b/test/ARCMT/rewrite-block-var.m new file mode 100644 index 000000000000..81407f92e9bb --- /dev/null +++ b/test/ARCMT/rewrite-block-var.m @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c -fobjc-runtime-has-weak %s.result +// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fobjc-nonfragile-abi -fblocks -fsyntax-only %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface Foo : NSObject +-(Foo *)something; +@end + +void bar(void (^block)()); + +void test1(Foo *p) { + __block Foo *x = p; // __block used just to break cycle. + bar(^{ + [x something]; + }); +} + +void test2(Foo *p) { + __block Foo *x; // __block used as output variable. + bar(^{ + x = [p something]; + }); +} diff --git a/test/ARCMT/rewrite-block-var.m.result b/test/ARCMT/rewrite-block-var.m.result new file mode 100644 index 000000000000..85093ac52d27 --- /dev/null +++ b/test/ARCMT/rewrite-block-var.m.result @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fblocks -fsyntax-only -fobjc-arc -x objective-c -fobjc-runtime-has-weak %s.result +// RUN: arcmt-test --args -triple x86_64-apple-macosx10.7 -fobjc-nonfragile-abi -fblocks -fsyntax-only %s > %t +// RUN: diff %t %s.result + +#include "Common.h" + +@interface Foo : NSObject +-(Foo *)something; +@end + +void bar(void (^block)()); + +void test1(Foo *p) { + __weak Foo *x = p; // __block used just to break cycle. + bar(^{ + [x something]; + }); +} + +void test2(Foo *p) { + __block Foo *x; // __block used as output variable. + bar(^{ + x = [p something]; + }); +} diff --git a/test/ARCMT/safe-arc-assign.m b/test/ARCMT/safe-arc-assign.m new file mode 100644 index 000000000000..368c2b6639da --- /dev/null +++ b/test/ARCMT/safe-arc-assign.m @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +void test12(id collection) { + for (id x in collection) { + x = 0; + x = 0; + } + + for (__strong id x in collection) { + x = 0; + } +} diff --git a/test/ARCMT/safe-arc-assign.m.result b/test/ARCMT/safe-arc-assign.m.result new file mode 100644 index 000000000000..cacd1093c524 --- /dev/null +++ b/test/ARCMT/safe-arc-assign.m.result @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: arcmt-test --args -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fsyntax-only -x objective-c %s > %t +// RUN: diff %t %s.result + +void test12(id collection) { + for (__strong id x in collection) { + x = 0; + x = 0; + } + + for (__strong id x in collection) { + x = 0; + } +} diff --git a/test/ARCMT/with-arc-mode-check.m b/test/ARCMT/with-arc-mode-check.m new file mode 100644 index 000000000000..c3146ef3b614 --- /dev/null +++ b/test/ARCMT/with-arc-mode-check.m @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -arcmt-check -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s + +@protocol NSObject +- (oneway void)release; +@end + +void test1(id p) { + [p release]; +} diff --git a/test/ARCMT/with-arc-mode-migrate.m b/test/ARCMT/with-arc-mode-migrate.m new file mode 100644 index 000000000000..3851bc77312e --- /dev/null +++ b/test/ARCMT/with-arc-mode-migrate.m @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc %s +// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result +// RUN: rm -rf %t + +@protocol NSObject +- (oneway void)release; +@end + +void test1(id p) { + [p release]; +} diff --git a/test/ARCMT/with-arc-mode-migrate.m.result b/test/ARCMT/with-arc-mode-migrate.m.result new file mode 100644 index 000000000000..c2222ae7b659 --- /dev/null +++ b/test/ARCMT/with-arc-mode-migrate.m.result @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: %clang_cc1 -arcmt-migrate -arcmt-migrate-directory %t -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc %s +// RUN: c-arcmt-test -arcmt-migrate-directory %t | arcmt-test -verify-transformed-files %s.result +// RUN: rm -rf %t + +@protocol NSObject +- (oneway void)release; +@end + +void test1(id p) { +} diff --git a/test/ARCMT/with-arc-mode-modify.m b/test/ARCMT/with-arc-mode-modify.m new file mode 100644 index 000000000000..17a398072e1f --- /dev/null +++ b/test/ARCMT/with-arc-mode-modify.m @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: cp %s %t +// RUN: %clang_cc1 -arcmt-modify -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %t +// RUN: diff %t %s.result +// RUN: rm %t + +@protocol NSObject +- (oneway void)release; +@end + +void test1(id p) { + [p release]; +} diff --git a/test/ARCMT/with-arc-mode-modify.m.result b/test/ARCMT/with-arc-mode-modify.m.result new file mode 100644 index 000000000000..97c5128cd950 --- /dev/null +++ b/test/ARCMT/with-arc-mode-modify.m.result @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %s.result +// RUN: cp %s %t +// RUN: %clang_cc1 -arcmt-modify -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -x objective-c %t +// RUN: diff %t %s.result +// RUN: rm %t + +@protocol NSObject +- (oneway void)release; +@end + +void test1(id p) { +} diff --git a/test/Analysis/bstring.c b/test/Analysis/bstring.c index 68bbb1a5b2f5..cd43e36d0351 100644 --- a/test/Analysis/bstring.c +++ b/test/Analysis/bstring.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.experimental.CString -analyzer-store=region -Wno-null-dereference -verify %s //===----------------------------------------------------------------------=== // Declarations @@ -64,14 +64,14 @@ void memcpy1 () { char src[] = {1, 2, 3, 4}; char dst[10]; - memcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}} + memcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void memcpy2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - memcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}} + memcpy(dst, src, 4); // expected-warning{{Memory copy function overflows destination buffer}} } void memcpy3 () { @@ -85,14 +85,14 @@ void memcpy4 () { char src[] = {1, 2, 3, 4}; char dst[10]; - memcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}} + memcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void memcpy5() { char src[] = {1, 2, 3, 4}; char dst[3]; - memcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}} + memcpy(dst+2, src+2, 2); // expected-warning{{Memory copy function overflows destination buffer}} } void memcpy6() { @@ -118,12 +118,12 @@ void memcpy9() { void memcpy10() { char a[4] = {0}; - memcpy(0, a, 4); // expected-warning{{Null pointer argument in call to byte string function}} + memcpy(0, a, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void memcpy11() { char a[4] = {0}; - memcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to byte string function}} + memcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void memcpy12() { @@ -144,7 +144,7 @@ void memcpy_unknown_size (size_t n) { void memcpy_unknown_size_warn (size_t n) { char a[4]; - if (memcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to byte string function}} + if (memcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to memory copy function}} (void)*(char*)0; // no-warning } @@ -186,14 +186,14 @@ void mempcpy1 () { char src[] = {1, 2, 3, 4}; char dst[10]; - mempcpy(dst, src, 5); // expected-warning{{Byte string function accesses out-of-bound array element}} + mempcpy(dst, src, 5); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void mempcpy2 () { char src[] = {1, 2, 3, 4}; char dst[1]; - mempcpy(dst, src, 4); // expected-warning{{Byte string function overflows destination buffer}} + mempcpy(dst, src, 4); // expected-warning{{Memory copy function overflows destination buffer}} } void mempcpy3 () { @@ -207,14 +207,14 @@ void mempcpy4 () { char src[] = {1, 2, 3, 4}; char dst[10]; - mempcpy(dst+2, src+2, 3); // expected-warning{{Byte string function accesses out-of-bound array element}} + mempcpy(dst+2, src+2, 3); // expected-warning{{Memory copy function accesses out-of-bound array element}} } void mempcpy5() { char src[] = {1, 2, 3, 4}; char dst[3]; - mempcpy(dst+2, src+2, 2); // expected-warning{{Byte string function overflows destination buffer}} + mempcpy(dst+2, src+2, 2); // expected-warning{{Memory copy function overflows destination buffer}} } void mempcpy6() { @@ -240,12 +240,12 @@ void mempcpy9() { void mempcpy10() { char a[4] = {0}; - mempcpy(0, a, 4); // expected-warning{{Null pointer argument in call to byte string function}} + mempcpy(0, a, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void mempcpy11() { char a[4] = {0}; - mempcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to byte string function}} + mempcpy(a, 0, 4); // expected-warning{{Null pointer argument in call to memory copy function}} } void mempcpy12() { @@ -260,7 +260,7 @@ void mempcpy13() { void mempcpy_unknown_size_warn (size_t n) { char a[4]; - if (mempcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to byte string function}} + if (mempcpy(a, 0, n) != a) // expected-warning{{Null pointer argument in call to memory copy function}} (void)*(char*)0; // no-warning } diff --git a/test/Analysis/misc-ps-eager-assume.m b/test/Analysis/misc-ps-eager-assume.m index 649c4b07f543..4380a187b800 100644 --- a/test/Analysis/misc-ps-eager-assume.m +++ b/test/Analysis/misc-ps-eager-assume.m @@ -12,6 +12,7 @@ typedef struct _NSZone NSZone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end @interface NSObject <NSObject> {} + (id)alloc; +- (id)init; @end typedef struct {} NSFastEnumerationState; @protocol NSFastEnumeration - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len; diff --git a/test/Analysis/nullptr.cpp b/test/Analysis/nullptr.cpp index b74a5abcdfa4..6f78baebfe22 100644 --- a/test/Analysis/nullptr.cpp +++ b/test/Analysis/nullptr.cpp @@ -39,3 +39,11 @@ void foo4(void) { *np = 0; // no-warning } + +int pr10372(void *& x) { + // GNU null is a pointer-sized integer, not a pointer. + x = __null; + // This used to crash. + return __null; +} + diff --git a/test/Analysis/objc-arc.m b/test/Analysis/objc-arc.m new file mode 100644 index 000000000000..6b22fd099b06 --- /dev/null +++ b/test/Analysis/objc-arc.m @@ -0,0 +1,149 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core -analyzer-checker=deadcode -analyzer-store=region -verify -fblocks -analyzer-opt-analyze-nested-blocks -fobjc-nonfragile-abi -fobjc-arc %s + +typedef signed char BOOL; +typedef struct _NSZone NSZone; +@class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; + +@protocol NSObject +- (BOOL)isEqual:(id)object; +@end +@protocol NSCopying +- (id)copyWithZone:(NSZone *)zone; +@end +@protocol NSCoding +- (void)encodeWithCoder:(NSCoder *)aCoder; +@end +@interface NSObject <NSObject> {} ++ (id)alloc; +@end +typedef const struct __CFAllocator * CFAllocatorRef; +extern const CFAllocatorRef kCFAllocatorDefault; +typedef double CFTimeInterval; +typedef CFTimeInterval CFAbsoluteTime; +extern CFAbsoluteTime CFAbsoluteTimeGetCurrent(void); +typedef const struct __CFDate * CFDateRef; +extern CFDateRef CFDateCreate(CFAllocatorRef allocator, CFAbsoluteTime at); + +typedef const void* objc_objectptr_t; +__attribute__((ns_returns_retained)) id objc_retainedObject(objc_objectptr_t __attribute__((cf_consumed)) pointer); +__attribute__((ns_returns_not_retained)) id objc_unretainedObject(objc_objectptr_t pointer); + +// Test the analyzer is working at all. +void test_working() { + int *p = 0; + *p = 0xDEADBEEF; // expected-warning {{null}} +} + +// Test that in ARC mode that blocks are correctly automatically copied +// and not flagged as warnings by the analyzer. +typedef void (^Block)(void); +void testblock_bar(int x); + +Block testblock_foo(int x) { + Block b = ^{ testblock_bar(x); }; + return b; // no-warning +} + +Block testblock_baz(int x) { + return ^{ testblock_bar(x); }; // no-warning +} + +Block global_block; + +void testblock_qux(int x) { + global_block = ^{ testblock_bar(x); }; // no-warning +} + +// Test that Objective-C pointers are null initialized. +void test_nil_initialized() { + id x; + if (x == 0) + return; + int *p = 0; + *p = 0xDEADBEEF; // no-warning +} + +// Test that we don't flag leaks of Objective-C objects. +void test_alloc() { + [NSObject alloc]; // no-warning +} + +// Test that CF allocations are still caught as leaks. +void test_cf_leak() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); // expected-warning {{Potential leak}} + (void) date; +} + +// Test that 'init' methods do not try to claim ownerhip of an *unowned* allocated object +// in ARC mode. +@interface RDar9424890_A : NSObject +- (id)initWithCleaner:(int)pop mop:(NSString *)mop ; +- (RDar9424890_A *)rdar9424890:(NSString *)identifier; +@end +@interface RDar9424890_B : NSObject +@end +@implementation RDar9424890_B +- (RDar9424890_A *)obj:(RDar9424890_A *)obj { + static NSString *WhizFiz = @"WhizFiz"; + RDar9424890_A *cell = [obj rdar9424890:WhizFiz]; + if (cell == ((void*)0)) { + cell = [[RDar9424890_A alloc] initWithCleaner:0 mop:WhizFiz]; // no-warning + } + return cell; +} +@end + +// Test that dead store checking works in the prescence of "cleanups" in the AST. +void rdar9424882() { + id x = [NSObject alloc]; // expected-warning {{Value stored to 'x' during its initialization is never read}} +} + +// Test +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +- (id) self; +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +void from_cf() { + id obj1 = (__bridge_transfer id)CFCreateSomething(); // expected-warning{{never read}} + id obj2 = (__bridge_transfer NSString*)CFCreateString(); + [obj2 self]; // Add a use, to show we can use the object after it has been transfered. + id obj3 = (__bridge id)CFGetSomething(); + [obj3 self]; // Add a use, to show we can use the object after it has been bridged. + id obj4 = (__bridge NSString*)CFGetString(); // expected-warning{{never read}} + id obj5 = (__bridge id)CFCreateSomething(); // expected-warning{{never read}} expected-warning{{leak}} + id obj6 = (__bridge NSString*)CFCreateString(); // expected-warning{{never read}} expected-warning{{leak}} +} + +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); // expected-warning{{never read}} + CFStringRef cf2 = (__bridge_retained CFStringRef)CreateNSString(); // expected-warning{{never read}} + CFTypeRef cf3 = (__bridge CFTypeRef)CreateSomething(); // expected-warning{{never read}} + CFStringRef cf4 = (__bridge CFStringRef)CreateNSString(); // expected-warning{{never read}} +} + +void test_objc_retainedObject() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); + id x = objc_retainedObject(date); + (void) x; +} + +void test_objc_unretainedObject() { + CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); + CFDateRef date = CFDateCreate(0, t); // expected-warning {{Potential leak}} + id x = objc_unretainedObject(date); + (void) x; +} + diff --git a/test/Analysis/pr4209.m b/test/Analysis/pr4209.m index 1e0fd32fcd03..e3bc5cc14333 100644 --- a/test/Analysis/pr4209.m +++ b/test/Analysis/pr4209.m @@ -49,17 +49,17 @@ CMProfileLocation; GSEbayCategory *rootCategory; } - (NSMutableDictionary*)categoryDictionaryForCategoryID:(int)inID inRootTreeCategories:(NSMutableArray*)inRootTreeCategories; // expected-note {{method definition for 'categoryDictionaryForCategoryID:inRootTreeCategories:' not found}} --(NSString*) categoryID; // expected-note {{method definition for 'categoryID' not found}} +-(NSString*) categoryID; // expected-note {{method definition for 'categoryID' not found}} expected-note {{using}} @end @interface GSEbayCategory : NSObject <NSCoding> { } -- (int) categoryID; +- (int) categoryID; // expected-note {{also found}} - (GSEbayCategory *) parent; - (GSEbayCategory*) subcategoryWithID:(int) inID; @end @implementation GBCategoryChooserPanelController + (int) chooseCategoryIDFromCategories:(NSArray*) inCategories searchRequest:(GBSearchRequest*)inRequest parentWindow:(NSWindow*) inParent { // expected-warning {{incomplete implementation}} return 0; } - (void) addCategory:(EBayCategoryType*)inCategory toRootTreeCategory:(NSMutableArray*)inRootTreeCategories { - GSEbayCategory *category = [rootCategory subcategoryWithID:[[inCategory categoryID] intValue]]; + GSEbayCategory *category = [rootCategory subcategoryWithID:[[inCategory categoryID] intValue]]; // expected-warning {{multiple methods named 'categoryID' found}} if (rootCategory != category) { GSEbayCategory *parent = category; diff --git a/test/Analysis/retain-release-gc-only.m b/test/Analysis/retain-release-gc-only.m index cbf00a27721d..ee1a6b4b782e 100644 --- a/test/Analysis/retain-release-gc-only.m +++ b/test/Analysis/retain-release-gc-only.m @@ -113,6 +113,7 @@ NSFastEnumerationState; @end @interface NSAutoreleasePool : NSObject { } - (void)drain; +- (id)init; @end extern NSString * const NSBundleDidLoadNotification; typedef double NSTimeInterval; @interface NSDate : NSObject <NSCopying, NSCoding> - (NSTimeInterval)timeIntervalSinceReferenceDate; diff --git a/test/Analysis/retain-release-path-notes-gc.m b/test/Analysis/retain-release-path-notes-gc.m new file mode 100644 index 000000000000..21d314fa1541 --- /dev/null +++ b/test/Analysis/retain-release-path-notes-gc.m @@ -0,0 +1,74 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=basic -analyzer-output=text -fobjc-gc-only -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=region -analyzer-output=text -fobjc-gc-only -verify %s + +/*** +This file is for testing the path-sensitive notes for retain/release errors. +Its goal is to have simple branch coverage of any path-based diagnostics, +not to actually check all possible retain/release errors. + +This file is for notes that only appear in a GC-enabled analysis. +Non-specific and ref-count-only notes should go in retain-release-path-notes.m. +***/ + +@interface NSObject ++ (id)alloc; +- (id)init; +- (void)dealloc; + +- (Class)class; + +- (id)retain; +- (void)release; +- (void)autorelease; +@end + +@interface Foo : NSObject +- (id)methodWithValue; +@property(retain) id propertyValue; +@end + +typedef struct CFType *CFTypeRef; +CFTypeRef CFRetain(CFTypeRef); +void CFRelease(CFTypeRef); + +id NSMakeCollectable(CFTypeRef); +CFTypeRef CFMakeCollectable(CFTypeRef); + +CFTypeRef CFCreateSomething(); +CFTypeRef CFGetSomething(); + + +void creationViaCFCreate () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void makeCollectable () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected}} + CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +2 retain count}} + CFMakeCollectable(leaked); // expected-note{{In GC mode a call to 'CFMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. An object must have a 0 retain count to be garbage collected. After this call its retain count is +1.}} + NSMakeCollectable(leaked); // expected-note{{In GC mode a call to 'NSMakeCollectable' decrements an object's retain count and registers the object with the garbage collector. Since it now has a 0 retain count the object can be automatically collected by the garbage collector}} + CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count. The object is not eligible for garbage collection until the retain count reaches 0 again.}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void retainReleaseIgnored () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +0 retain count}} + [object retain]; // expected-note{{In GC mode the 'retain' message has no effect}} + [object release]; // expected-note{{In GC mode the 'release' message has no effect}} + [object autorelease]; // expected-note{{In GC mode an 'autorelease' has no effect}} + CFRelease((CFTypeRef)object); // expected-warning{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} expected-note{{Incorrect decrement of the reference count of an object that is not owned at this point by the caller}} +} + +@implementation Foo (FundamentalRuleUnderGC) +- (id)getViolation { + id object = (id) CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected.}} + return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' and returned from method 'getViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector}} +} + +- (id)copyViolation { + id object = (id) CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count. Core Foundation objects are not automatically garbage collected.}} + return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' and returned from method 'copyViolation' is potentially leaked when using garbage collection. Callers of this method do not expect a returned object with a +1 retain count since they expect the object to be managed by the garbage collector}} +} +@end + diff --git a/test/Analysis/retain-release-path-notes.m b/test/Analysis/retain-release-path-notes.m new file mode 100644 index 000000000000..bac0afb3f7ab --- /dev/null +++ b/test/Analysis/retain-release-path-notes.m @@ -0,0 +1,123 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=basic -analyzer-output=text -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -analyze -analyzer-checker=core,osx.coreFoundation.CFRetainRelease,osx.cocoa.ClassRelease -analyzer-store=region -analyzer-output=text -verify %s + +/*** +This file is for testing the path-sensitive notes for retain/release errors. +Its goal is to have simple branch coverage of any path-based diagnostics, +not to actually check all possible retain/release errors. + +This file includes notes that only appear in a ref-counted analysis. +GC-specific notes should go in retain-release-path-notes-gc.m. +***/ + +@interface NSObject ++ (id)alloc; +- (id)init; +- (void)dealloc; + +- (Class)class; + +- (id)retain; +- (void)release; +- (void)autorelease; +@end + +@interface Foo : NSObject +- (id)methodWithValue; +@property(retain) id propertyValue; +@end + +typedef struct CFType *CFTypeRef; +CFTypeRef CFRetain(CFTypeRef); +void CFRelease(CFTypeRef); + +id NSMakeCollectable(CFTypeRef); +CFTypeRef CFMakeCollectable(CFTypeRef); + +CFTypeRef CFCreateSomething(); +CFTypeRef CFGetSomething(); + + +void creationViaAlloc () { + id leaked = [[NSObject alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void creationViaCFCreate () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void acquisitionViaMethod (Foo *foo) { + id leaked = [foo methodWithValue]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +0 retain count}} + [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}} + [leaked retain]; // expected-note{{Reference count incremented. The object now has a +2 retain count}} + [leaked release]; // expected-note{{Reference count decremented. The object now has a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void acquisitionViaProperty (Foo *foo) { + id leaked = foo.propertyValue; // expected-warning{{leak}} expected-note{{Property returns an Objective-C object with a +0 retain count}} + [leaked retain]; // expected-note{{Reference count incremented. The object now has a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void acquisitionViaCFFunction () { + CFTypeRef leaked = CFGetSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object with a +0 retain count}} + CFRetain(leaked); // expected-note{{Reference count incremented. The object now has a +1 retain count}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +void explicitDealloc () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} + [object dealloc]; // expected-note{{Object released by directly sending the '-dealloc' message}} + [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}} +} + +void implicitDealloc () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} + [object release]; // expected-note{{Object released}} + [object class]; // expected-warning{{Reference-counted object is used after it is released}} // expected-note{{Reference-counted object is used after it is released}} +} + +void overAutorelease () { + id object = [[NSObject alloc] init]; // expected-note{{Method returns an Objective-C object with a +1 retain count}} + [object autorelease]; // expected-note{{Object sent -autorelease message}} + [object autorelease]; // expected-note{{Object sent -autorelease message}} + return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease 2 times but the object has a +1 retain count}} +} + +void autoreleaseUnowned (Foo *foo) { + id object = foo.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} + [object autorelease]; // expected-note{{Object sent -autorelease message}} + return; // expected-warning{{Object sent -autorelease too many times}} expected-note{{Object over-autoreleased: object was sent -autorelease but the object has a +0 retain count}} +} + +void makeCollectableIgnored () { + CFTypeRef leaked = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain count}} + CFMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'CFMakeCollectable' has no effect on its argument}} + NSMakeCollectable(leaked); // expected-note{{When GC is not enabled a call to 'NSMakeCollectable' has no effect on its argument}} + return; // expected-note{{Object leaked: object allocated and stored into 'leaked' is not referenced later in this execution path and has a retain count of +1}} +} + +CFTypeRef CFCopyRuleViolation () { + CFTypeRef object = CFGetSomething(); // expected-note{{Call to function 'CFGetSomething' returns a Core Foundation object with a +0 retain counte}} + return object; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} +} + +CFTypeRef CFGetRuleViolation () { + CFTypeRef object = CFCreateSomething(); // expected-warning{{leak}} expected-note{{Call to function 'CFCreateSomething' returns a Core Foundation object with a +1 retain counte}} + return object; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'object' is return from a function whose name ('CFGetRuleViolation') does not contain 'Copy' or 'Create'. This violates the naming convention rules given the Memory Management Guide for Core Foundation}} +} + +@implementation Foo (FundamentalMemoryManagementRules) +- (id)copyViolation { + id result = self.propertyValue; // expected-note{{Property returns an Objective-C object with a +0 retain count}} + return result; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} expected-note{{Object returned to caller with a +0 retain count}} expected-note{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} +} + +- (id)getViolation { + id result = [[Foo alloc] init]; // expected-warning{{leak}} expected-note{{Method returns an Objective-C object with a +1 retain count}} + return result; // expected-note{{Object returned to caller as an owning reference (single retain count transferred to caller)}} expected-note{{Object leaked: object allocated and stored into 'result' is returned from a method whose name ('getViolation') does not start with 'copy', 'mutableCopy', 'alloc' or 'new'. This violates the naming convention rules given in the Memory Management Guide for Cocoa}} +} +@end diff --git a/test/Analysis/retain-release.m b/test/Analysis/retain-release.m index 7af14f129c3f..71ae756cf045 100644 --- a/test/Analysis/retain-release.m +++ b/test/Analysis/retain-release.m @@ -116,6 +116,7 @@ typedef struct _NSZone NSZone; - (id)retain; - (oneway void)release; - (id)autorelease; +- (id)init; @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end @protocol NSMutableCopying - (id)mutableCopyWithZone:(NSZone *)zone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @@ -517,7 +518,7 @@ void f17(int x, CFTypeRef p) { @implementation TestReturnNotOwnedWhenExpectedOwned - (NSString*)newString { NSString *s = [NSString stringWithUTF8String:"hello"]; - return s; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}} + return s; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } @end @@ -735,7 +736,7 @@ typedef CFTypeRef OtherRef; - (id)initReturningNewClassBad2 { [self release]; self = [[RDar6320065Subclass alloc] init]; - return [self autorelease]; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}} + return [self autorelease]; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } @end @@ -1302,7 +1303,7 @@ CFDateRef returnsRetainedCFDate() { } - (CFDateRef) newCFRetainedAsCFNoAttr { - return (CFDateRef)[(id)[self returnsCFRetainedAsCF] autorelease]; // expected-warning{{Object with +0 retain counts returned to caller where a +1 (owning) retain count is expected}} + return (CFDateRef)[(id)[self returnsCFRetainedAsCF] autorelease]; // expected-warning{{Object with a +0 retain count returned to caller where a +1 (owning) retain count is expected}} } - (NSDate*) alsoReturnsRetained { @@ -1445,7 +1446,7 @@ static void rdar_8724287(CFErrorRef error) while (error_to_dump != ((void*)0)) { CFDictionaryRef info; - info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1448 and stored into 'info'}} + info = CFErrorCopyUserInfo(error_to_dump); // expected-warning{{Potential leak of an object allocated on line 1449 and stored into 'info'}} if (info != ((void*)0)) { } @@ -1461,3 +1462,81 @@ extern void rdar_9234108_helper(void *key, void * CF_CONSUMED value); void rdar_9234108() { rdar_9234108_helper(0, CFStringCreate()); } + +// <rdar://problem/9726279> - Make sure that objc_method_family works +// to override naming conventions. +struct TwoDoubles { + double one; + double two; +}; +typedef struct TwoDoubles TwoDoubles; + +@interface NSValue (Mine) +- (id)_prefix_initWithTwoDoubles:(TwoDoubles)twoDoubles __attribute__((objc_method_family(init))); +@end + +@implementation NSValue (Mine) +- (id)_prefix_initWithTwoDoubles:(TwoDoubles)twoDoubles +{ + return [self init]; +} +@end + +void rdar9726279() { + TwoDoubles twoDoubles = { 0.0, 0.0 }; + NSValue *value = [[NSValue alloc] _prefix_initWithTwoDoubles:twoDoubles]; + [value release]; +} + +// <rdar://problem/9732321> +// Test camelcase support for CF conventions. While Core Foundation APIs +// don't use camel casing, other code is allowed to use it. +CFArrayRef camelcase_create_1() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camelcase_createno() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + +CFArrayRef camelcase_copy() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camelcase_copying() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + +CFArrayRef copyCamelCase() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef __copyCamelCase() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef __createCamelCase() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camel_create() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + + +CFArrayRef camel_creat() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + +CFArrayRef camel_copy() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camel_copyMachine() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // no-warning +} + +CFArrayRef camel_copymachine() { + return CFArrayCreateMutable(0, 10, &kCFTypeArrayCallBacks); // expected-warning {{leak}} +} + diff --git a/test/Analysis/retain-release.mm b/test/Analysis/retain-release.mm index 0faeb1a2a300..bdc3dc03964a 100644 --- a/test/Analysis/retain-release.mm +++ b/test/Analysis/retain-release.mm @@ -121,6 +121,7 @@ typedef struct _NSZone NSZone; + (id)allocWithZone:(NSZone *)zone; + (id)alloc; - (void)dealloc; +- (id)init; @end @interface NSObject (NSCoderMethods) - (id)awakeAfterUsingCoder:(NSCoder *)aDecoder; diff --git a/test/Analysis/string-fail.c b/test/Analysis/string-fail.c new file mode 100644 index 000000000000..64ac504d6d80 --- /dev/null +++ b/test/Analysis/string-fail.c @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// XFAIL: * + +// This file is for tests that may eventually go into string.c, or may be +// deleted outright. At one point these tests passed, but only because we +// weren't correctly modelling the behavior of the relevant string functions. +// The tests aren't incorrect, but require the analyzer to be smarter about +// conjured values than it currently is. + +//===----------------------------------------------------------------------=== +// Declarations +//===----------------------------------------------------------------------=== + +// Some functions are so similar to each other that they follow the same code +// path, such as memcpy and __memcpy_chk, or memcmp and bcmp. If VARIANT is +// defined, make sure to use the variants instead to make sure they are still +// checked by the analyzer. + +// Some functions are implemented as builtins. These should be #defined as +// BUILTIN(f), which will prepend "__builtin_" if USE_BUILTINS is defined. + +// Functions that have variants and are also available as builtins should be +// declared carefully! See memcpy() for an example. + +#ifdef USE_BUILTINS +# define BUILTIN(f) __builtin_ ## f +#else /* USE_BUILTINS */ +# define BUILTIN(f) f +#endif /* USE_BUILTINS */ + +#define NULL 0 +typedef typeof(sizeof(int)) size_t; + + +//===----------------------------------------------------------------------=== +// strnlen() +//===----------------------------------------------------------------------=== + +#define strnlen BUILTIN(strnlen) +size_t strnlen(const char *s, size_t maxlen); + +void strnlen_liveness(const char *x) { + if (strnlen(x, 10) < 5) + return; + if (strnlen(x, 10) < 5) + (void)*(char*)0; // no-warning +} + +void strnlen_subregion() { + struct two_stringsn { char a[2], b[2]; }; + extern void use_two_stringsn(struct two_stringsn *); + + struct two_stringsn z; + use_two_stringsn(&z); + + size_t a = strnlen(z.a, 10); + z.b[0] = 5; + size_t b = strnlen(z.a, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_two_stringsn(&z); + + size_t c = strnlen(z.a, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern void use_stringn(char *); +void strnlen_argument(char *x) { + size_t a = strnlen(x, 10); + size_t b = strnlen(x, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + use_stringn(x); + + size_t c = strnlen(x, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +extern char global_strn[]; +void strnlen_global() { + size_t a = strnlen(global_strn, 10); + size_t b = strnlen(global_strn, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + // Call a function with unknown effects, which should invalidate globals. + use_stringn(0); + + size_t c = strnlen(global_strn, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} + +void strnlen_indirect(char *x) { + size_t a = strnlen(x, 10); + char *p = x; + char **p2 = &p; + size_t b = strnlen(x, 10); + if (a == 0 && b != 0) + (void)*(char*)0; // expected-warning{{never executed}} + + extern void use_stringn_ptr(char*const*); + use_stringn_ptr(p2); + + size_t c = strnlen(x, 10); + if (a == 0 && c != 0) + (void)*(char*)0; // expected-warning{{null}} +} diff --git a/test/Analysis/string.c b/test/Analysis/string.c index e6baf51c474c..dc97e1a119e0 100644 --- a/test/Analysis/string.c +++ b/test/Analysis/string.c @@ -1,7 +1,7 @@ -// RUN: %clang_cc1 -analyze -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s -// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,cplusplus.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DVARIANT -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s +// RUN: %clang_cc1 -analyze -DUSE_BUILTINS -DVARIANT -analyzer-checker=core,unix.experimental.CString,deadcode.experimental.UnreachableCode -analyzer-store=region -Wno-null-dereference -verify %s //===----------------------------------------------------------------------=== // Declarations @@ -55,16 +55,16 @@ void strlen_constant2(char x) { } size_t strlen_null() { - return strlen(0); // expected-warning{{Null pointer argument in call to byte string function}} + return strlen(0); // expected-warning{{Null pointer argument in call to string length function}} } size_t strlen_fn() { - return strlen((char*)&strlen_fn); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}} + return strlen((char*)&strlen_fn); // expected-warning{{Argument to string length function is the address of the function 'strlen_fn', which is not a null-terminated string}} } size_t strlen_nonloc() { label: - return strlen((char*)&&label); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}} + return strlen((char*)&&label); // expected-warning{{Argument to string length function is the address of the label 'label', which is not a null-terminated string}} } void strlen_subregion() { @@ -143,24 +143,23 @@ void strlen_liveness(const char *x) { // strnlen() //===----------------------------------------------------------------------=== -#define strnlen BUILTIN(strnlen) size_t strnlen(const char *s, size_t maxlen); void strnlen_constant0() { if (strnlen("123", 10) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant1() { const char *a = "123"; if (strnlen(a, 10) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant2(char x) { char a[] = "123"; if (strnlen(a, 10) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} a[0] = x; if (strnlen(a, 10) != 3) (void)*(char*)0; // expected-warning{{null}} @@ -168,107 +167,90 @@ void strnlen_constant2(char x) { void strnlen_constant4() { if (strnlen("123456", 3) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant5() { const char *a = "123456"; if (strnlen(a, 3) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} } void strnlen_constant6(char x) { char a[] = "123456"; if (strnlen(a, 3) != 3) - (void)*(char*)0; // no-warning + (void)*(char*)0; // expected-warning{{never executed}} a[0] = x; if (strnlen(a, 3) != 3) (void)*(char*)0; // expected-warning{{null}} } size_t strnlen_null() { - return strnlen(0, 3); // expected-warning{{Null pointer argument in call to byte string function}} + return strnlen(0, 3); // expected-warning{{Null pointer argument in call to string length function}} } size_t strnlen_fn() { - return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to byte string function is the address of the function 'strlen_fn', which is not a null-terminated string}} + return strnlen((char*)&strlen_fn, 3); // expected-warning{{Argument to string length function is the address of the function 'strlen_fn', which is not a null-terminated string}} } size_t strnlen_nonloc() { label: - return strnlen((char*)&&label, 3); // expected-warning{{Argument to byte string function is the address of the label 'label', which is not a null-terminated string}} + return strnlen((char*)&&label, 3); // expected-warning{{Argument to string length function is the address of the label 'label', which is not a null-terminated string}} } -void strnlen_subregion() { - struct two_stringsn { char a[2], b[2]; }; - extern void use_two_stringsn(struct two_stringsn *); - - struct two_stringsn z; - use_two_stringsn(&z); - - size_t a = strnlen(z.a, 10); - z.b[0] = 5; - size_t b = strnlen(z.a, 10); - if (a == 0 && b != 0) +void strnlen_zero() { + if (strnlen("abc", 0) != 0) (void)*(char*)0; // expected-warning{{never executed}} + if (strnlen(NULL, 0) != 0) // no-warning + (void)*(char*)0; // no-warning +} + +size_t strnlen_compound_literal() { + // This used to crash because we don't model the string lengths of + // compound literals. + return strnlen((char[]) { 'a', 'b', 0 }, 1); +} - use_two_stringsn(&z); +size_t strnlen_unknown_limit(float f) { + // This used to crash because we don't model the integer values of floats. + return strnlen("abc", (int)f); +} - size_t c = strnlen(z.a, 10); - if (a == 0 && c != 0) +void strnlen_is_not_strlen(char *x) { + if (strnlen(x, 10) != strlen(x)) (void)*(char*)0; // expected-warning{{null}} } -extern void use_stringn(char *); -void strnlen_argument(char *x) { - size_t a = strnlen(x, 10); - size_t b = strnlen(x, 10); - if (a == 0 && b != 0) +void strnlen_at_limit(char *x) { + size_t len = strnlen(x, 10); + if (len > 10) (void)*(char*)0; // expected-warning{{never executed}} - - use_stringn(x); - - size_t c = strnlen(x, 10); - if (a == 0 && c != 0) - (void)*(char*)0; // expected-warning{{null}} + if (len == 10) + (void)*(char*)0; // expected-warning{{null}} } -extern char global_strn[]; -void strnlen_global() { - size_t a = strnlen(global_strn, 10); - size_t b = strnlen(global_strn, 10); - if (a == 0 && b != 0) +void strnlen_less_than_limit(char *x) { + size_t len = strnlen(x, 10); + if (len > 10) (void)*(char*)0; // expected-warning{{never executed}} - - // Call a function with unknown effects, which should invalidate globals. - use_stringn(0); - - size_t c = strnlen(global_str, 10); - if (a == 0 && c != 0) - (void)*(char*)0; // expected-warning{{null}} + if (len < 10) + (void)*(char*)0; // expected-warning{{null}} } -void strnlen_indirect(char *x) { - size_t a = strnlen(x, 10); - char *p = x; - char **p2 = &p; - size_t b = strnlen(x, 10); - if (a == 0 && b != 0) +void strnlen_at_actual(size_t limit) { + size_t len = strnlen("abc", limit); + if (len > 3) (void)*(char*)0; // expected-warning{{never executed}} - - extern void use_stringn_ptr(char*const*); - use_stringn_ptr(p2); - - size_t c = strnlen(x, 10); - if (a == 0 && c != 0) + if (len == 3) (void)*(char*)0; // expected-warning{{null}} } -void strnlen_liveness(const char *x) { - if (strnlen(x, 10) < 5) - return; - if (strnlen(x, 10) < 5) - (void)*(char*)0; // no-warning +void strnlen_less_than_actual(size_t limit) { + size_t len = strnlen("abc", limit); + if (len > 3) + (void)*(char*)0; // expected-warning{{never executed}} + if (len < 3) + (void)*(char*)0; // expected-warning{{null}} } //===----------------------------------------------------------------------=== @@ -291,15 +273,15 @@ char *strcpy(char *restrict s1, const char *restrict s2); void strcpy_null_dst(char *x) { - strcpy(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}} + strcpy(NULL, x); // expected-warning{{Null pointer argument in call to string copy function}} } void strcpy_null_src(char *x) { - strcpy(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}} + strcpy(x, NULL); // expected-warning{{Null pointer argument in call to string copy function}} } void strcpy_fn(char *x) { - strcpy(x, (char*)&strcpy_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcpy_fn', which is not a null-terminated string}} + strcpy(x, (char*)&strcpy_fn); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}} } void strcpy_effects(char *x, char *y) { @@ -318,7 +300,7 @@ void strcpy_effects(char *x, char *y) { void strcpy_overflow(char *y) { char x[4]; if (strlen(y) == 4) - strcpy(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcpy(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcpy_no_overflow(char *y) { @@ -362,7 +344,7 @@ void stpcpy_effect(char *x, char *y) { void stpcpy_overflow(char *y) { char x[4]; if (strlen(y) == 4) - stpcpy(x, y); // expected-warning{{Byte string function overflows destination buffer}} + stpcpy(x, y); // expected-warning{{String copy function overflows destination buffer}} } void stpcpy_no_overflow(char *y) { @@ -391,15 +373,15 @@ char *strcat(char *restrict s1, const char *restrict s2); void strcat_null_dst(char *x) { - strcat(NULL, x); // expected-warning{{Null pointer argument in call to byte string function}} + strcat(NULL, x); // expected-warning{{Null pointer argument in call to string copy function}} } void strcat_null_src(char *x) { - strcat(x, NULL); // expected-warning{{Null pointer argument in call to byte string function}} + strcat(x, NULL); // expected-warning{{Null pointer argument in call to string copy function}} } void strcat_fn(char *x) { - strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to byte string function is the address of the function 'strcat_fn', which is not a null-terminated string}} + strcat(x, (char*)&strcat_fn); // expected-warning{{Argument to string copy function is the address of the function 'strcat_fn', which is not a null-terminated string}} } void strcat_effects(char *y) { @@ -415,27 +397,24 @@ void strcat_effects(char *y) { if ((int)strlen(x) != (orig_len + strlen(y))) (void)*(char*)0; // no-warning - - if (a != x[0]) - (void)*(char*)0; // expected-warning{{null}} } void strcat_overflow_0(char *y) { char x[4] = "12"; if (strlen(y) == 4) - strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcat(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcat_overflow_1(char *y) { char x[4] = "12"; if (strlen(y) == 3) - strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcat(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcat_overflow_2(char *y) { char x[4] = "12"; if (strlen(y) == 2) - strcat(x, y); // expected-warning{{Byte string function overflows destination buffer}} + strcat(x, y); // expected-warning{{String copy function overflows destination buffer}} } void strcat_no_overflow(char *y) { @@ -444,6 +423,136 @@ void strcat_no_overflow(char *y) { strcat(x, y); // no-warning } +void strcat_symbolic_dst_length(char *dst) { + strcat(dst, "1234"); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +void strcat_symbolic_src_length(char *src) { + char dst[8] = "1234"; + strcat(dst, src); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +void strcat_unknown_src_length(char *src, int offset) { + char dst[8] = "1234"; + strcat(dst, &src[offset]); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +// There is no strcat_unknown_dst_length because if we can't get a symbolic +// length for the "before" strlen, we won't be able to set one for "after". + +void strcat_too_big(char *dst, char *src) { + if (strlen(dst) != (((size_t)0) - 2)) + return; + if (strlen(src) != 2) + return; + strcat(dst, src); // expected-warning{{This expression will create a string whose length is too big to be represented as a size_t}} +} + + +//===----------------------------------------------------------------------=== +// strncpy() +//===----------------------------------------------------------------------=== + +#ifdef VARIANT + +#define __strncpy_chk BUILTIN(__strncpy_chk) +char *__strncpy_chk(char *restrict s1, const char *restrict s2, size_t n, size_t destlen); + +#define strncpy(a,b,n) __strncpy_chk(a,b,n,(size_t)-1) + +#else /* VARIANT */ + +#define strncpy BUILTIN(strncpy) +char *strncpy(char *restrict s1, const char *restrict s2, size_t n); + +#endif /* VARIANT */ + + +void strncpy_null_dst(char *x) { + strncpy(NULL, x, 5); // expected-warning{{Null pointer argument in call to string copy function}} +} + +void strncpy_null_src(char *x) { + strncpy(x, NULL, 5); // expected-warning{{Null pointer argument in call to string copy function}} +} + +void strncpy_fn(char *x) { + strncpy(x, (char*)&strcpy_fn, 5); // expected-warning{{Argument to string copy function is the address of the function 'strcpy_fn', which is not a null-terminated string}} +} + +void strncpy_effects(char *x, char *y) { + char a = x[0]; + + if (strncpy(x, y, 5) != x) + (void)*(char*)0; // no-warning + + if (strlen(x) != strlen(y)) + (void)*(char*)0; // expected-warning{{null}} + + if (a != x[0]) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncpy_overflow(char *y) { + char x[4]; + if (strlen(y) == 4) + strncpy(x, y, 5); // expected-warning{{Size argument is greater than the length of the destination buffer}} +} + +void strncpy_no_overflow(char *y) { + char x[4]; + if (strlen(y) == 3) + strncpy(x, y, 5); // expected-warning{{Size argument is greater than the length of the destination buffer}} +} + +void strncpy_no_overflow2(char *y, int n) { + if (n <= 4) + return; + + char x[4]; + if (strlen(y) == 3) + strncpy(x, y, n); // expected-warning{{Size argument is greater than the length of the destination buffer}} +} + +void strncpy_truncate(char *y) { + char x[4]; + if (strlen(y) == 4) + strncpy(x, y, 3); // no-warning +} + +void strncpy_no_truncate(char *y) { + char x[4]; + if (strlen(y) == 3) + strncpy(x, y, 3); // no-warning +} + +void strncpy_exactly_matching_buffer(char *y) { + char x[4]; + strncpy(x, y, 4); // no-warning + + // strncpy does not null-terminate, so we have no idea what the strlen is + // after this. + if (strlen(x) > 4) + (void)*(int*)0; // expected-warning{{null}} +} + +void strncpy_exactly_matching_buffer2(char *y) { + if (strlen(y) >= 4) + return; + + char x[4]; + strncpy(x, y, 4); // no-warning + + // This time, we know that y fits in x anyway. + if (strlen(x) > 3) + (void)*(int*)0; // no-warning +} //===----------------------------------------------------------------------=== // strncat() @@ -465,15 +574,15 @@ char *strncat(char *restrict s1, const char *restrict s2, size_t n); void strncat_null_dst(char *x) { - strncat(NULL, x, 4); // expected-warning{{Null pointer argument in call to byte string function}} + strncat(NULL, x, 4); // expected-warning{{Null pointer argument in call to string copy function}} } void strncat_null_src(char *x) { - strncat(x, NULL, 4); // expected-warning{{Null pointer argument in call to byte string function}} + strncat(x, NULL, 4); // expected-warning{{Null pointer argument in call to string copy function}} } void strncat_fn(char *x) { - strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to byte string function is the address of the function 'strncat_fn', which is not a null-terminated string}} + strncat(x, (char*)&strncat_fn, 4); // expected-warning{{Argument to string copy function is the address of the function 'strncat_fn', which is not a null-terminated string}} } void strncat_effects(char *y) { @@ -489,33 +598,30 @@ void strncat_effects(char *y) { if (strlen(x) != orig_len + strlen(y)) (void)*(char*)0; // no-warning - - if (a != x[0]) - (void)*(char*)0; // expected-warning{{null}} } void strncat_overflow_0(char *y) { char x[4] = "12"; if (strlen(y) == 4) - strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, strlen(y)); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_overflow_1(char *y) { char x[4] = "12"; if (strlen(y) == 3) - strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, strlen(y)); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_overflow_2(char *y) { char x[4] = "12"; if (strlen(y) == 2) - strncat(x, y, strlen(y)); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, strlen(y)); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_overflow_3(char *y) { char x[4] = "12"; if (strlen(y) == 4) - strncat(x, y, 2); // expected-warning{{Byte string function overflows destination buffer}} + strncat(x, y, 2); // expected-warning{{Size argument is greater than the free space in the destination buffer}} } void strncat_no_overflow_1(char *y) { char x[5] = "12"; @@ -529,12 +635,69 @@ void strncat_no_overflow_2(char *y) { strncat(x, y, 1); // no-warning } +void strncat_symbolic_dst_length(char *dst) { + strncat(dst, "1234", 5); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning +} + +void strncat_symbolic_src_length(char *src) { + char dst[8] = "1234"; + strncat(dst, src, 3); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + + char dst2[8] = "1234"; + strncat(dst2, src, 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}} +} + +void strncat_unknown_src_length(char *src, int offset) { + char dst[8] = "1234"; + strncat(dst, &src[offset], 3); + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + + char dst2[8] = "1234"; + strncat(dst2, &src[offset], 4); // expected-warning{{Size argument is greater than the free space in the destination buffer}} +} + +// There is no strncat_unknown_dst_length because if we can't get a symbolic +// length for the "before" strlen, we won't be able to set one for "after". + +void strncat_symbolic_limit(unsigned limit) { + char dst[6] = "1234"; + char src[] = "567"; + strncat(dst, src, limit); // no-warning + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + if (strlen(dst) == 4) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncat_unknown_limit(float limit) { + char dst[6] = "1234"; + char src[] = "567"; + strncat(dst, src, (size_t)limit); // no-warning + if (strlen(dst) < 4) + (void)*(char*)0; // no-warning + if (strlen(dst) == 4) + (void)*(char*)0; // expected-warning{{null}} +} + +void strncat_too_big(char *dst, char *src) { + if (strlen(dst) != (((size_t)0) - 2)) + return; + if (strlen(src) != 2) + return; + strncat(dst, src, 2); // expected-warning{{This expression will create a string whose length is too big to be represented as a size_t}} +} + //===----------------------------------------------------------------------=== // strcmp() //===----------------------------------------------------------------------=== #define strcmp BUILTIN(strcmp) -int strcmp(const char *restrict s1, const char *restrict s2); +int strcmp(const char * s1, const char * s2); void strcmp_constant0() { if (strcmp("123", "123") != 0) @@ -577,13 +740,13 @@ void strcmp_2() { void strcmp_null_0() { char *x = NULL; char *y = "123"; - strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcmp_null_1() { char *x = "123"; char *y = NULL; - strcmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcmp_diff_length_0() { @@ -614,12 +777,22 @@ void strcmp_diff_length_3() { (void)*(char*)0; // no-warning } +void strcmp_embedded_null () { + if (strcmp("\0z", "\0y") != 0) + (void)*(char*)0; // no-warning +} + +void strcmp_unknown_arg (char *unknown) { + if (strcmp(unknown, unknown) != 0) + (void)*(char*)0; // no-warning +} + //===----------------------------------------------------------------------=== // strncmp() //===----------------------------------------------------------------------=== #define strncmp BUILTIN(strncmp) -int strncmp(const char *restrict s1, const char *restrict s2, size_t n); +int strncmp(const char *s1, const char *s2, size_t n); void strncmp_constant0() { if (strncmp("123", "123", 3) != 0) @@ -662,13 +835,13 @@ void strncmp_2() { void strncmp_null_0() { char *x = NULL; char *y = "123"; - strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncmp_null_1() { char *x = "123"; char *y = NULL; - strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncmp_diff_length_0() { @@ -720,12 +893,17 @@ void strncmp_diff_length_6() { (void)*(char*)0; // no-warning } +void strncmp_embedded_null () { + if (strncmp("ab\0zz", "ab\0yy", 4) != 0) + (void)*(char*)0; // no-warning +} + //===----------------------------------------------------------------------=== // strcasecmp() //===----------------------------------------------------------------------=== #define strcasecmp BUILTIN(strcasecmp) -int strcasecmp(const char *restrict s1, const char *restrict s2); +int strcasecmp(const char *s1, const char *s2); void strcasecmp_constant0() { if (strcasecmp("abc", "Abc") != 0) @@ -768,13 +946,13 @@ void strcasecmp_2() { void strcasecmp_null_0() { char *x = NULL; char *y = "123"; - strcasecmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcasecmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcasecmp_null_1() { char *x = "123"; char *y = NULL; - strcasecmp(x, y); // expected-warning{{Null pointer argument in call to byte string function}} + strcasecmp(x, y); // expected-warning{{Null pointer argument in call to string comparison function}} } void strcasecmp_diff_length_0() { @@ -805,12 +983,17 @@ void strcasecmp_diff_length_3() { (void)*(char*)0; // no-warning } +void strcasecmp_embedded_null () { + if (strcasecmp("ab\0zz", "ab\0yy") != 0) + (void)*(char*)0; // no-warning +} + //===----------------------------------------------------------------------=== // strncasecmp() //===----------------------------------------------------------------------=== #define strncasecmp BUILTIN(strncasecmp) -int strncasecmp(const char *restrict s1, const char *restrict s2, size_t n); +int strncasecmp(const char *s1, const char *s2, size_t n); void strncasecmp_constant0() { if (strncasecmp("abc", "Abc", 3) != 0) @@ -853,13 +1036,13 @@ void strncasecmp_2() { void strncasecmp_null_0() { char *x = NULL; char *y = "123"; - strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncasecmp_null_1() { char *x = "123"; char *y = NULL; - strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to byte string function}} + strncasecmp(x, y, 3); // expected-warning{{Null pointer argument in call to string comparison function}} } void strncasecmp_diff_length_0() { @@ -910,3 +1093,8 @@ void strncasecmp_diff_length_6() { if (strncasecmp(x, y, 3) != 1) (void)*(char*)0; // no-warning } + +void strncasecmp_embedded_null () { + if (strncasecmp("ab\0zz", "ab\0yy", 4) != 0) + (void)*(char*)0; // no-warning +} diff --git a/test/Analysis/uninit-ps-rdar6145427.m b/test/Analysis/uninit-ps-rdar6145427.m index f1794068d5e1..f4df91396a84 100644 --- a/test/Analysis/uninit-ps-rdar6145427.m +++ b/test/Analysis/uninit-ps-rdar6145427.m @@ -11,7 +11,10 @@ typedef struct _NSZone NSZone; @protocol NSObject - (BOOL)isEqual:(id)object; @end @protocol NSCopying - (id)copyWithZone:(NSZone *)zone; @end @protocol NSCoding - (void)encodeWithCoder:(NSCoder *)aCoder; @end -@interface NSObject <NSObject> {} + (id)alloc; @end +@interface NSObject <NSObject> {} ++ (id)alloc; +- (id)init; +@end extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); @interface NSValue : NSObject <NSCopying, NSCoding> - (void)getValue:(void *)value; @end @class NSString, NSData; diff --git a/test/Analysis/uninit-vals-ps-region.m b/test/Analysis/uninit-vals-ps-region.m index 1700f54dbfb0..c62818a12e0d 100644 --- a/test/Analysis/uninit-vals-ps-region.m +++ b/test/Analysis/uninit-vals-ps-region.m @@ -67,3 +67,12 @@ void rdar_7780304() { b.x |= 1; // expected-warning{{The left expression of the compound assignment is an uninitialized value. The computed value will also be garbage}} } + +// The flip side of PR10163 -- float arrays that are actually uninitialized +// (The main test is in uninit-vals.m) +void test_PR10163(float); +void PR10163 (void) { + float x[2]; + test_PR10163(x[1]); // expected-warning{{uninitialized value}} +} + diff --git a/test/Analysis/uninit-vals.m b/test/Analysis/uninit-vals.m index 2cd5e0c11848..4b7e6f42cf8f 100644 --- a/test/Analysis/uninit-vals.m +++ b/test/Analysis/uninit-vals.m @@ -23,3 +23,12 @@ NSUInteger f8(A* x){ return n; } + + +// PR10163 -- don't warn for default-initialized float arrays. +// (An additional test is in uninit-vals-ps-region.m) +void test_PR10163(float); +void PR10163 (void) { + float x[2] = {0}; + test_PR10163(x[1]); // no-warning +} diff --git a/test/Analysis/variadic-method-types.m b/test/Analysis/variadic-method-types.m index 018956ab1b2d..03c309a5004a 100644 --- a/test/Analysis/variadic-method-types.m +++ b/test/Analysis/variadic-method-types.m @@ -73,13 +73,13 @@ void f(id a, id<P> b, C* c, C<P> *d, FooType fooType, BarType barType) { [NSDictionary dictionaryWithObjectsAndKeys:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSDictionary' method 'dictionaryWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} [NSSet setWithObjects:@"Foo", "Bar", nil]; // expected-warning {{Argument to 'NSSet' method 'setWithObjects:' should be an Objective-C pointer type, not 'char *'}} - [[[NSArray alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} - [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} + [[[NSArray alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to 'NSArray' method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} + [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to 'NSDictionary' method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'char *'}} [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", (void*) 0, nil] autorelease]; // no-warning [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", kCGImageSourceShouldCache, nil] autorelease]; // no-warning [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", fooType, nil] autorelease]; // no-warning - [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", barType, nil] autorelease]; // expected-warning {{Argument to method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'BarType'}} - [[[NSSet alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} + [[[NSDictionary alloc] initWithObjectsAndKeys:@"Foo", barType, nil] autorelease]; // expected-warning {{Argument to 'NSDictionary' method 'initWithObjectsAndKeys:' should be an Objective-C pointer type, not 'BarType'}} + [[[NSSet alloc] initWithObjects:@"Foo", "Bar", nil] autorelease]; // expected-warning {{Argument to 'NSSet' method 'initWithObjects:' should be an Objective-C pointer type, not 'char *'}} } // This previously crashed the variadic argument checker. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e35413aaf8cc..0f00e5075a52 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -99,10 +99,10 @@ if(PYTHONINTERP_FOUND) COMMENT "Running Clang and LLVM regression tests") add_dependencies(check-all clang-test.deps) if ( LLVM_INCLUDE_TESTS ) - add_dependencies(check-all check.deps ClangUnitTests) + add_dependencies(clang-test.deps check.deps ClangUnitTests) endif ( LLVM_INCLUDE_TESTS ) add_dependencies(clang-test.deps - llvm-dis opt + llvm-dis llc opt FileCheck count not ) set_target_properties(check-all PROPERTIES FOLDER "Clang tests") @@ -110,7 +110,7 @@ if(PYTHONINTERP_FOUND) add_dependencies(clang-test clang-test.deps) add_dependencies(clang-test.deps - clang clang-headers c-index-test + clang clang-headers c-index-test arcmt-test c-arcmt-test ) endif() diff --git a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp index ccadf416c977..15d86b7740c4 100644 --- a/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp +++ b/test/CXX/basic/basic.lookup/basic.lookup.argdep/p4.cpp @@ -19,8 +19,9 @@ namespace D { } namespace C { - class C {}; - void func(C); + class C {}; // expected-note {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B::B' to 'const C::C &' for 1st argument}} + void func(C); // expected-note {{'C::func' declared here}} \ + // expected-note {{passing argument to parameter here}} C operator+(C,C); D::D operator+(D::D,D::D); } @@ -32,7 +33,13 @@ namespace D { namespace Test { void test() { func(A::A()); - func(B::B()); // expected-error {{use of undeclared identifier 'func'}} + // FIXME: namespace-aware typo correction causes an extra, misleading + // message in this case; some form of backtracking, diagnostic message + // delaying, or argument checking before emitting diagnostics is needed to + // avoid accepting and printing out a typo correction that proves to be + // incorrect once argument-dependent lookup resolution has occurred. + func(B::B()); // expected-error {{use of undeclared identifier 'func'; did you mean 'C::func'?}} \ + // expected-error {{no viable conversion from 'B::B' to 'C::C'}} func(C::C()); A::A() + A::A(); B::B() + B::B(); diff --git a/test/CXX/class/p6-0x.cpp b/test/CXX/class/p6-0x.cpp new file mode 100644 index 000000000000..fc83e065fa6f --- /dev/null +++ b/test/CXX/class/p6-0x.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x + +class Trivial { int n; void f(); }; +class NonTrivial1 { NonTrivial1(const NonTrivial1 &); }; +class NonTrivial2 { NonTrivial2(NonTrivial2 &&); }; +class NonTrivial3 { NonTrivial3 operator=(const NonTrivial3 &); }; +class NonTrivial4 { NonTrivial4 operator=(NonTrivial4 &&); }; +class NonTrivial5 { ~NonTrivial5(); }; + +static_assert(__is_trivial(Trivial), "Trivial is not trivial"); +static_assert(!__is_trivial(NonTrivial1), "NonTrivial1 is trivial"); +static_assert(!__is_trivial(NonTrivial2), "NonTrivial2 is trivial"); +static_assert(!__is_trivial(NonTrivial3), "NonTrivial3 is trivial"); +static_assert(!__is_trivial(NonTrivial4), "NonTrivial4 is trivial"); +static_assert(!__is_trivial(NonTrivial5), "NonTrivial5 is trivial"); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp index 40917b8ae3dc..cbb439ef5fec 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.stc/p1.cpp @@ -7,13 +7,13 @@ template<typename T> void f(T) {} template<typename T> static void g(T) {} -template<> static void f<int>(int); // expected-error{{explicit specialization cannot have a storage class}} +template<> static void f<int>(int); // expected-error{{explicit specialization has extraneous, inconsistent storage class 'static'}} template static void f<float>(float); // expected-error{{explicit instantiation cannot have a storage class}} template<> void f<double>(double); template void f<long>(long); -template<> static void g<int>(int); // expected-error{{explicit specialization cannot have a storage class}} +template<> static void g<int>(int); // expected-warning{{explicit specialization cannot have a storage class}} template static void g<float>(float); // expected-error{{explicit instantiation cannot have a storage class}} template<> void g<double>(double); diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp index fabfb5329da1..54da854dab55 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p5.cpp @@ -46,7 +46,7 @@ void j() { U<auto> v; // expected-error{{'auto' not allowed in template argument}} int n; - (void)dynamic_cast<auto&>(S()); // expected-error{{'auto' not allowed here}} + (void)dynamic_cast<auto&>(n); // expected-error{{'auto' not allowed here}} (void)static_cast<auto*>(&n); // expected-error{{'auto' not allowed here}} (void)reinterpret_cast<auto*>(&n); // expected-error{{'auto' not allowed here}} (void)const_cast<auto>(n); // expected-error{{'auto' not allowed here}} diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp index 06aeaa690a1a..8b4b70398471 100644 --- a/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp +++ b/test/CXX/dcl.dcl/dcl.spec/dcl.type/dcl.spec.auto/p6.cpp @@ -54,8 +54,7 @@ void f() { auto *fail1 = 0; // expected-error {{variable 'fail1' with type 'auto *' has incompatible initializer of type 'int'}} int **p; - // FIXME: due to PR9233, we get the wrong diagnostic here. - const auto **fail2(p); // desired-error {{variable 'fail2' with type 'auto const **' has incompatible initializer of type 'int **'}} expected-error {{cannot initialize}} + const auto **fail2(p); // expected-error {{variable 'fail2' with type 'auto const **' has incompatible initializer of type 'int **'}} } struct S { diff --git a/test/CXX/except/except.spec/canonical.cpp b/test/CXX/except/except.spec/canonical.cpp new file mode 100644 index 000000000000..9bc26de14440 --- /dev/null +++ b/test/CXX/except/except.spec/canonical.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +// PR10087: Make sure that we don't conflate exception specifications +// from different functions in the canonical type system. +namespace std +{ + +template <class _Tp> _Tp&& declval() noexcept; + +template <class _Tp, class... _Args> +struct __is_nothrow_constructible +{ + static const bool value = noexcept(_Tp(declval<_Args>()...)); +}; + +template<class, class _Traits, class _Allocator> +class basic_string +{ +public: + typedef typename _Traits::char_type value_type; + typedef _Allocator allocator_type; + + basic_string() + noexcept(__is_nothrow_constructible<allocator_type>::value); +}; + +template <class, class, class _Compare> +struct __map_value_compare +{ +public: + __map_value_compare() + noexcept(__is_nothrow_constructible<_Compare>::value); +}; + +struct less +{ +}; + +struct map +{ + typedef __map_value_compare<int, short, less> __vc; + __vc vc_; +}; + + +template<class T, class _Traits, class _Allocator> +basic_string<T, _Traits, _Allocator>::basic_string() noexcept(__is_nothrow_constructible<allocator_type>::value) {} + +template <class T, class Value, class _Compare> +__map_value_compare<T, Value, _Compare>::__map_value_compare() + noexcept(__is_nothrow_constructible<_Compare>::value) {} + +} // std diff --git a/test/CXX/except/except.spec/p14-ir.cpp b/test/CXX/except/except.spec/p14-ir.cpp index c681727e8ccf..81fbf7d97005 100644 --- a/test/CXX/except/except.spec/p14-ir.cpp +++ b/test/CXX/except/except.spec/p14-ir.cpp @@ -26,12 +26,12 @@ struct X4 { struct X5 : X0, X4 { }; void test(X2 x2, X3 x3, X5 x5) { - // CHECK: define linkonce_odr void @_ZN2X2C1ERKS_(%struct.X0* %this, %struct.X0*) unnamed_addr + // CHECK: define linkonce_odr void @_ZN2X2C1ERKS_(%struct.X2* %this, %struct.X2*) unnamed_addr // CHECK: call void @_ZN2X2C2ERKS_({{.*}}) nounwind // CHECK-NEXT: ret void // CHECK-NEXT: } X2 x2a(x2); - // CHECK: define linkonce_odr void @_ZN2X3C1ERKS_(%struct.X0* %this, %struct.X0*) unnamed_addr + // CHECK: define linkonce_odr void @_ZN2X3C1ERKS_(%struct.X3* %this, %struct.X3*) unnamed_addr // CHECK: call void @_ZN2X3C2ERKS_({{.*}}) nounwind // CHECK-NEXT: ret void // CHECK-NEXT: } @@ -55,24 +55,25 @@ struct X8 : X6 { }; struct X9 : X6, X7 { }; void test() { - // CHECK: define linkonce_odr void @_ZN2X8C1Ev(%struct.X0* %this) unnamed_addr + // CHECK: define linkonce_odr void @_ZN2X8C1Ev(%struct.X8* %this) unnamed_addr // CHECK: call void @_ZN2X8C2Ev({{.*}}) nounwind // CHECK-NEXT: ret void X8(); - // CHECK: define linkonce_odr void @_ZN2X9C1Ev(%struct.X0* %this) unnamed_addr + // CHECK: define linkonce_odr void @_ZN2X9C1Ev(%struct.X9* %this) unnamed_addr // FIXME: check that this is the end of the line here: // CHECK: call void @_ZN2X9C2Ev({{.*}}) // CHECK-NEXT: ret void X9(); - // CHECK: define linkonce_odr void @_ZN2X9C2Ev(%struct.X0* %this) unnamed_addr + // CHECK: define linkonce_odr void @_ZN2X9C2Ev(%struct.X9* %this) unnamed_addr // CHECK: call void @_ZN2X6C2Ev({{.*}}) nounwind // FIXME: and here: + // CHECK-NEXT: bitcast // CHECK-NEXT: call void @_ZN2X7C2Ev({{.*}}) // CHECK: ret void - // CHECK: define linkonce_odr void @_ZN2X8C2Ev(%struct.X0* %this) unnamed_addr + // CHECK: define linkonce_odr void @_ZN2X8C2Ev(%struct.X8* %this) unnamed_addr // CHECK: call void @_ZN2X6C2Ev({{.*}}) nounwind // CHECK-NEXT: ret void } diff --git a/test/CXX/special/class.copy/p33-0x.cpp b/test/CXX/special/class.copy/p33-0x.cpp index 1e6a025ef31b..b196865c8225 100644 --- a/test/CXX/special/class.copy/p33-0x.cpp +++ b/test/CXX/special/class.copy/p33-0x.cpp @@ -23,3 +23,35 @@ void throw_move_only(X x) { throw x2; } +namespace PR10142 { + struct X { + X(); + X(X&&); + X(const X&) = delete; // expected-note 2{{function has been explicitly marked deleted here}} + }; + + void f(int i) { + X x; + try { + X x2; + if (i) + throw x2; // okay + throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} + } catch (...) { + } + } + + template<typename T> + void f2(int i) { + T x; + try { + T x2; + if (i) + throw x2; // okay + throw x; // expected-error{{call to deleted constructor of 'PR10142::X'}} + } catch (...) { + } + } + + template void f2<X>(int); // expected-note{{in instantiation of function template specialization 'PR10142::f2<PR10142::X>' requested here}} +} diff --git a/test/CXX/stmt.stmt/stmt.dcl/p3-0x.cpp b/test/CXX/stmt.stmt/stmt.dcl/p3-0x.cpp index b41504488e8e..40b4c23841c8 100644 --- a/test/CXX/stmt.stmt/stmt.dcl/p3-0x.cpp +++ b/test/CXX/stmt.stmt/stmt.dcl/p3-0x.cpp @@ -30,13 +30,13 @@ struct Y { void f(); void test_Y() { - goto end; - Y y; + goto end; // expected-error{{goto into protected scope}} + Y y; // expected-note{{jump bypasses variable with a non-trivial destructor}} end: f(); - goto inner; + goto inner; // expected-error{{goto into protected scope}} { - Y y2; + Y y2; // expected-note{{jump bypasses variable with a non-trivial destructor}} inner: f(); } diff --git a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp index 7bfa6fe58266..0c92f62582cf 100644 --- a/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp +++ b/test/CXX/stmt.stmt/stmt.iter/stmt.ranged/p1.cpp @@ -129,7 +129,7 @@ void g() { void *begin(); // expected-note {{selected 'begin' function with iterator type 'void *'}} void *end(); }; - for (auto u : NoIncr()) { // expected-error {{arithmetic on pointer to void type}} + for (auto u : NoIncr()) { // expected-error {{arithmetic on a pointer to void type}} } struct NoNotEq { diff --git a/test/CXX/temp/p3.cpp b/test/CXX/temp/p3.cpp new file mode 100644 index 000000000000..16ebb381fd12 --- /dev/null +++ b/test/CXX/temp/p3.cpp @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -verify %s + +template<typename T> struct S { + static int a, b; +}; + +template<typename T> int S<T>::a, S<T>::b; // expected-error {{can only declare a single entity}} + +// FIXME: the last two diagnostics here are terrible. +template<typename T> struct A { static A a; } A<T>::a; // expected-error {{expected ';' after struct}} \ + expected-error {{use of undeclared identifier 'T'}} \ + expected-error {{cannot name the global scope}} \ + expected-error {{no member named 'a' in the global namespace}} + +template<typename T> struct B { } f(); // expected-error {{expected ';' after struct}} \ + expected-error {{requires a type specifier}} + +template<typename T> struct C { } // expected-error {{expected ';' after struct}} + +A<int> c; diff --git a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp index 3c22cf349c9f..295f080a75d9 100644 --- a/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp +++ b/test/CXX/temp/temp.fct.spec/temp.deduct/temp.deduct.call/p3.cpp @@ -71,7 +71,21 @@ void test_f3(int ***ip, volatile int ***vip) { A<int> a0 = f3(ip); A<volatile int> a1 = f3(vip); } - + +// Also accept conversions for pointer types which require removing +// [[noreturn]]. +namespace noreturn_stripping { + template <class R> + void f(R (*function)()); + + void g() __attribute__ ((__noreturn__)); + void h(); + void test() { + f(g); + f(h); + } +} + // - If P is a class, and P has the form template-id, then A can be a // derived class of the deduced A. Likewise, if P is a pointer to a class // of the form template-id, A can be a pointer to a derived class pointed @@ -123,3 +137,12 @@ namespace N { N::F<T1>(d); // OK } } + +namespace PR9233 { + template<typename T> void f(const T **q); // expected-note{{candidate template ignored: substitution failure [with T = int]}} + + void g(int **p) { + f(p); // expected-error{{no matching function for call to 'f'}} + } + +} diff --git a/test/CXX/temp/temp.res/temp.local/p3.cpp b/test/CXX/temp/temp.res/temp.local/p3.cpp index 88f8963e6b62..54da8856fe46 100644 --- a/test/CXX/temp/temp.res/temp.local/p3.cpp +++ b/test/CXX/temp/temp.res/temp.local/p3.cpp @@ -22,11 +22,11 @@ template <class T> struct Derived: Base<int>, Base<char> { namespace PR6717 { template <typename T> class WebVector { - } + } // expected-error {{expected ';' after class}} - WebVector(const WebVector<T>& other) { } + WebVector(const WebVector<T>& other) { } // expected-error{{undeclared identifier 'T'}} \ + expected-error{{requires a type specifier}} template <typename C> - WebVector<T>& operator=(const C& other) { } // expected-error{{unknown type name 'WebVector'}} \ - // expected-error{{unqualified-id}} + WebVector<T>& operator=(const C& other) { } // expected-error{{undeclared identifier 'T'}} } diff --git a/test/CXX/temp/temp.spec/temp.explicit/p7.cpp b/test/CXX/temp/temp.spec/temp.explicit/p7.cpp index b62e0cb9b1cd..7398dca7ba1c 100644 --- a/test/CXX/temp/temp.spec/temp.explicit/p7.cpp +++ b/test/CXX/temp/temp.spec/temp.explicit/p7.cpp @@ -7,14 +7,14 @@ struct X0 { }; T* f0(T* ptr) { - return ptr + 1; // expected-error{{pointer to function}} + return ptr + 1; // expected-error{{pointer to the function}} } static T* static_member; }; template<typename T> -T* X0<T>::static_member = ((T*)0) + 1; // expected-error{{pointer to function}} +T* X0<T>::static_member = ((T*)0) + 1; // expected-error{{pointer to the function}} template class X0<int>; // okay diff --git a/test/CodeGen/annotate.c b/test/CodeGen/annotate.c index ffaeebbca515..9ed187d1d170 100644 --- a/test/CodeGen/annotate.c +++ b/test/CodeGen/annotate.c @@ -7,4 +7,4 @@ void a(char *a) { // CHECK: private unnamed_addr global // CHECK: private unnamed_addr global -// CHECK: @llvm.global.annotations = appending global [2 x %0] +// CHECK: @llvm.global.annotations = appending global [2 x { i8*, i8*, i8*, i32 }] diff --git a/test/CodeGen/arm-arguments.c b/test/CodeGen/arm-arguments.c index 73bc03dac7f5..081bc89dcda7 100644 --- a/test/CodeGen/arm-arguments.c +++ b/test/CodeGen/arm-arguments.c @@ -61,7 +61,7 @@ struct s10 { int f0; int : 0; int : 0; }; struct s10 f10(void) {} // APCS-GNU: define void @f11( -// APCS-GNU: struct.s10* sret +// APCS-GNU: struct.s11* sret // AAPCS: define arm_aapcscc i32 @f11() struct s11 { int : 0; int f0; }; struct s11 f11(void) {} @@ -80,7 +80,7 @@ struct s13 { float f0; }; struct s13 f13(void) {} // APCS-GNU: define void @f14( -// APCS-GNU: struct.s13* sret +// APCS-GNU: union.u14* sret // AAPCS: define arm_aapcscc i32 @f14() union u14 { float f0; }; union u14 f14(void) {} diff --git a/test/CodeGen/arm-asm-variable.c b/test/CodeGen/arm-asm-variable.c new file mode 100644 index 000000000000..865d1971e8b9 --- /dev/null +++ b/test/CodeGen/arm-asm-variable.c @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin9 -emit-llvm -w -o - %s | FileCheck %s + +typedef long long int64_t; +typedef unsigned int uint32_t; + +int64_t foo(int64_t v, volatile int64_t *p) +{ + register uint32_t rl asm("r1"); + register uint32_t rh asm("r2"); + + int64_t r; + uint32_t t; + + __asm__ __volatile__( \ + "ldrexd%[_rl], %[_rh], [%[_p]]" \ + : [_rl] "=&r" (rl), [_rh] "=&r" (rh) \ + : [_p] "p" (p) : "memory"); + + // CHECK: call { i32, i32 } asm sideeffect "ldrexd$0, $1, [$2]", "={r1},={r2},r,~{memory}"(i64* + + return r; +} + +// Make sure we translate register names properly. +void bar (void) { + register unsigned int rn asm("r14"); + register unsigned int d asm("r2"); + + // CHECK: call i32 asm sideeffect "sub $1, $1, #32", "={r2},{lr}" + asm volatile ("sub %1, %1, #32" : "=r"(d) : "r"(rn)); +} diff --git a/test/CodeGen/asm-errors.c b/test/CodeGen/asm-errors.c index cd4d1ffa528d..438c82ba74db 100644 --- a/test/CodeGen/asm-errors.c +++ b/test/CodeGen/asm-errors.c @@ -1,6 +1,8 @@ // REQUIRES: x86-registered-target -// RUN: not %clang_cc1 -triple i386-apple-darwin10 -emit-obj %s -o /dev/null > %t 2>&1 -// RUN: FileCheck %s < %t + +// RUN: true +// UN: not %clang_cc1 -triple i386-apple-darwin10 -emit-obj %s -o /dev/null > %t 2>&1 +// UN: FileCheck %s < %t int test1(int X) { // CHECK: error: invalid instruction mnemonic 'abc' diff --git a/test/CodeGen/asm-inout.c b/test/CodeGen/asm-inout.c index 29142f7c5592..ce524fe70fe9 100644 --- a/test/CodeGen/asm-inout.c +++ b/test/CodeGen/asm-inout.c @@ -21,7 +21,7 @@ void test2() { // PR7338 void test3(int *vout, int vin) { - // CHECK: call void asm "opr $0,$1", "=*r|m|r,r|m|r,~{di},~{dirflag},~{fpsr},~{flags}" + // CHECK: call void asm "opr $0,$1", "=*r|m|r,r|m|r,~{edi},~{dirflag},~{fpsr},~{flags}" asm( "opr %[vout],%[vin]" : [vout] "=r,=m,=r" (*vout) @@ -37,4 +37,3 @@ int test4(volatile int *addr) { return (int)oldval; // CHECK: call i8 asm "frob $0", "=r,0{{.*}}"(i8 -1) } - diff --git a/test/CodeGen/asm.c b/test/CodeGen/asm.c index eb112858718c..7199f09e1a3c 100644 --- a/test/CodeGen/asm.c +++ b/test/CodeGen/asm.c @@ -49,10 +49,10 @@ unsigned t9(unsigned int a) { // PR3908 void t10(int r) { __asm__("PR3908 %[lf] %[xx] %[li] %[r]" : [r] "+r" (r) : [lf] "mx" (0), [li] "mr" (0), [xx] "x" ((double)(0))); - + // CHECK: @t10( // CHECK:PR3908 $1 $3 $2 $0 -} +} // PR3373 @@ -119,7 +119,7 @@ int t16() { void t17() { int i; __asm__ ( "nop": "=m"(i)); - + // CHECK: @t17() // CHECK: call void asm "nop", "=*m, } @@ -127,7 +127,7 @@ void t17() { // <rdar://problem/6841383> int t18(unsigned data) { int a, b; - + asm("xyz" :"=a"(a), "=d"(b) : "a"(data)); return a + b; // CHECK: t18(i32 @@ -140,7 +140,7 @@ int t18(unsigned data) { // PR6780 int t19(unsigned data) { int a, b; - + asm("x{abc|def|ghi}z" :"=r"(a): "r"(data)); return a + b; // CHECK: t19(i32 @@ -153,7 +153,7 @@ double t20(double x) { register long double result; __asm __volatile ("frndint" : "=t" (result) : "0" (x)); return result; - + // CHECK: @t20 // CHECK: fpext double {{.*}} to x86_fp80 // CHECK-NEXT: call x86_fp80 asm sideeffect "frndint" @@ -190,3 +190,17 @@ unsigned char t23(unsigned char a, unsigned char b) { "edx", "cc"); return res; } + + +// PR10299 - fpsr, fpcr +void test(void) +{ + __asm__ __volatile__( \ + "finit" \ + : \ + : \ + :"st","st(1)","st(2)","st(3)", \ + "st(4)","st(5)","st(6)","st(7)", \ + "fpsr","fpcr" \ + ); +} diff --git a/test/CodeGen/attr-weak-import.c b/test/CodeGen/attr-weak-import.c new file mode 100644 index 000000000000..0707f59a608b --- /dev/null +++ b/test/CodeGen/attr-weak-import.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-darwin-apple -emit-llvm -o - %s | FileCheck %s +// rdar://9538608 + +extern int A __attribute__((weak_import)); +int A; + +extern int B __attribute__((weak_import)); +extern int B; + +int C; +extern int C __attribute__((weak_import)); + +extern int D __attribute__((weak_import)); +extern int D __attribute__((weak_import)); +int D; + +extern int E __attribute__((weak_import)); +int E; +extern int E __attribute__((weak_import)); + +// CHECK: @A = global i32 +// CHECK-NOT: @B = +// CHECK: @C = common global i32 +// CHECK: @D = global i32 +// CHECK: @E = global i32 + diff --git a/test/CodeGen/attributes.c b/test/CodeGen/attributes.c index 770ce766dfba..4e73af60439d 100644 --- a/test/CodeGen/attributes.c +++ b/test/CodeGen/attributes.c @@ -4,7 +4,7 @@ // CHECK: @t5 = weak global i32 2 int t5 __attribute__((weak)) = 2; -// CHECK: @t13 = global %0 zeroinitializer, section "SECT" +// CHECK: @t13 = global %struct.s0 zeroinitializer, section "SECT" struct s0 { int x; }; struct s0 t13 __attribute__((section("SECT"))) = { 0 }; diff --git a/test/CodeGen/bitfield-2.c b/test/CodeGen/bitfield-2.c index 8de432faa8ae..69ed5b11fe35 100644 --- a/test/CodeGen/bitfield-2.c +++ b/test/CodeGen/bitfield-2.c @@ -11,7 +11,7 @@ // CHECK-RECORD: *** Dumping IRgen Record Layout // CHECK-RECORD: Record: struct s0 // CHECK-RECORD: Layout: <CGRecordLayout -// CHECK-RECORD: LLVMType:<{ [3 x i8] }> +// CHECK-RECORD: LLVMType:%struct.s0 = type <{ [3 x i8] }> // CHECK-RECORD: IsZeroInitializable:1 // CHECK-RECORD: BitFields:[ // CHECK-RECORD: <CGBitFieldInfo Size:24 IsSigned:1 @@ -56,7 +56,7 @@ unsigned long long test_0() { // CHECK-RECORD: *** Dumping IRgen Record Layout // CHECK-RECORD: Record: struct s1 // CHECK-RECORD: Layout: <CGRecordLayout -// CHECK-RECORD: LLVMType:<{ [2 x i8], i8 }> +// CHECK-RECORD: LLVMType:%struct.s1 = type <{ [2 x i8], i8 }> // CHECK-RECORD: IsZeroInitializable:1 // CHECK-RECORD: BitFields:[ // CHECK-RECORD: <CGBitFieldInfo Size:10 IsSigned:1 @@ -113,7 +113,7 @@ unsigned long long test_1() { // CHECK-RECORD: *** Dumping IRgen Record Layout // CHECK-RECORD: Record: union u2 // CHECK-RECORD: Layout: <CGRecordLayout -// CHECK-RECORD: LLVMType:<{ i8 }> +// CHECK-RECORD: LLVMType:%union.u2 = type <{ i8 }> // CHECK-RECORD: IsZeroInitializable:1 // CHECK-RECORD: BitFields:[ // CHECK-RECORD: <CGBitFieldInfo Size:3 IsSigned:0 @@ -288,7 +288,7 @@ _Bool test_6() { // CHECK-RECORD: *** Dumping IRgen Record Layout // CHECK-RECORD: Record: struct s7 // CHECK-RECORD: Layout: <CGRecordLayout -// CHECK-RECORD: LLVMType:{ i32, i32, i32, i8, [3 x i8], [4 x i8], [12 x i8] } +// CHECK-RECORD: LLVMType:%struct.s7 = type { i32, i32, i32, i8, [3 x i8], [4 x i8], [12 x i8] } // CHECK-RECORD: IsZeroInitializable:1 // CHECK-RECORD: BitFields:[ // CHECK-RECORD: <CGBitFieldInfo Size:5 IsSigned:1 diff --git a/test/CodeGen/blocksignature.c b/test/CodeGen/blocksignature.c index 7526f19468ed..63fe1248a86d 100644 --- a/test/CodeGen/blocksignature.c +++ b/test/CodeGen/blocksignature.c @@ -2,13 +2,13 @@ // RUN: %clang_cc1 -fblocks -triple i686-apple-darwin9 %s -emit-llvm -o - | FileCheck %s -check-prefix=X32 // X64: @.str = private unnamed_addr constant [6 x i8] c"v8@?0\00" -// X64: @__block_literal_global = internal constant %1 { i8** @_NSConcreteGlobalBlock, i32 1342177280, +// X64: @__block_literal_global = internal constant {{.*}} { i8** @_NSConcreteGlobalBlock, i32 1342177280, // X64: @.str1 = private unnamed_addr constant [12 x i8] c"i16@?0c8f12\00" // X64: store i32 1073741824, i32* // X32: [[STR1:@.*]] = private unnamed_addr constant [6 x i8] c"v4@?0\00" -// X32: @__block_descriptor_tmp = internal constant [[FULL_DESCRIPTOR_T:%.*]] { i32 0, i32 20, i8* getelementptr inbounds ([6 x i8]* [[STR1]], i32 0, i32 0), i8* null } -// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:%.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @__block_global_{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) } +// X32: @__block_descriptor_tmp = internal constant [[FULL_DESCRIPTOR_T:.*]] { i32 0, i32 20, i8* getelementptr inbounds ([6 x i8]* [[STR1]], i32 0, i32 0), i8* null } +// X32: @__block_literal_global = internal constant [[GLOBAL_LITERAL_T:.*]] { i8** @_NSConcreteGlobalBlock, i32 1342177280, i32 0, i8* bitcast (void (i8*)* @__block_global_{{.*}} to i8*), [[DESCRIPTOR_T:%.*]]* bitcast ([[FULL_DESCRIPTOR_T]]* @__block_descriptor_tmp to {{%.*}}*) } // X32: [[STR2:@.*]] = private unnamed_addr constant [11 x i8] c"i12@?0c4f8\00" // X32: @__block_descriptor_tmp{{.*}} = internal constant [[FULL_DESCRIPTOR_T]] { i32 0, i32 24, i8* getelementptr inbounds ([11 x i8]* [[STR2]], i32 0, i32 0), i8* null } // X32: store i32 1073741824, i32* diff --git a/test/CodeGen/blockstret.c b/test/CodeGen/blockstret.c index e49b52a18853..d5dae6f3a614 100644 --- a/test/CodeGen/blockstret.c +++ b/test/CodeGen/blockstret.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin9 %s -emit-llvm -o - | FileCheck %s -check-prefix=X64 // RUN: %clang_cc1 -fblocks -triple i686-apple-darwin9 %s -emit-llvm -o - | FileCheck %s -check-prefix=X32 -// X64: internal constant {{%.*}} { i8** @_NSConcreteGlobalBlock, i32 1879048192 +// X64: internal constant {{.*}} { i8** @_NSConcreteGlobalBlock, i32 1879048192 // X64: store i32 1610612736, i32* %want // X32: @_NSConcreteGlobalBlock, i32 1879048192, i32 0, diff --git a/test/CodeGen/builtin-expect.c b/test/CodeGen/builtin-expect.c index 88479d90a092..664c6b6a470e 100644 --- a/test/CodeGen/builtin-expect.c +++ b/test/CodeGen/builtin-expect.c @@ -19,3 +19,29 @@ int main() { // CHECK: call void @isigprocmask() // CHECK: [[C:%.*]] = call i64 (...)* @bar() + + +// CHECK: @test1 +int test1(int x) { +// CHECK: @llvm.expect + if (__builtin_expect (x, 1)) + return 0; + return x; +} + +// CHECK: @test2 +int test2(int x) { +// CHECK: @llvm.expect + switch(__builtin_expect(x, 5)) { + default: + return 0; + case 0: + case 1: + case 2: + return 1; + case 5: + return 5; + }; + + return 0; +} diff --git a/test/CodeGen/byval-memcpy-elim.c b/test/CodeGen/byval-memcpy-elim.c index 8aa08fb07b2c..76cdafb5e8c5 100644 --- a/test/CodeGen/byval-memcpy-elim.c +++ b/test/CodeGen/byval-memcpy-elim.c @@ -18,3 +18,36 @@ void test1a(struct Test1S, struct Test2S); void test1(struct Test1S *A, struct Test2S *B) { test1a(*A, *B); } + +// The above gets tricker when the byval argument requires higher alignment +// than the natural alignment of the type in question. +// rdar://9483886 + +// Make sure we do generate a memcpy when we cannot guarantee alignment. +struct Test3S { + int a,b,c,d,e,f,g,h,i,j,k,l; +}; +void test2a(struct Test3S q); +// CHECK: define void @test2( +// CHECK: alloca %struct.Test3S, align 8 +// CHECK: memcpy +// CHECK: call void @test2a +void test2(struct Test3S *q) { + test2a(*q); +} + +// But make sure we don't generate a memcpy when we can guarantee alignment. +void fooey(void); +// CHECK: define void @test3( +// CHECK: alloca %struct.Test3S, align 8 +// CHECK: call void @fooey +// CHECK-NOT: memcpy +// CHECK: call void @test2a +// CHECK-NOT: memcpy +// CHECK: call void @test2a +void test3(struct Test3S a) { + struct Test3S b = a; + fooey(); + test2a(a); + test2a(b); +} diff --git a/test/CodeGen/call-knr-indirect.c b/test/CodeGen/call-knr-indirect.c deleted file mode 100644 index 2e923b303cd0..000000000000 --- a/test/CodeGen/call-knr-indirect.c +++ /dev/null @@ -1,11 +0,0 @@ -// RUN: %clang %s -O0 -emit-llvm -S -o - | grep 'call.*rb_define_global_function' -// This should call rb_define_global_function, not rb_f_chop. - -void rb_define_global_function (const char*,void(*)(),int); -static void rb_f_chop(); -void Init_String() { - rb_define_global_function("chop", rb_f_chop, 0); -} -static void rb_f_chop() { -} - diff --git a/test/CodeGen/call.c b/test/CodeGen/call.c new file mode 100644 index 000000000000..ef327751b3ad --- /dev/null +++ b/test/CodeGen/call.c @@ -0,0 +1,39 @@ +// RUN: %clang %s -O0 -emit-llvm -S -o - | FileCheck %s + +// This should call rb_define_global_function, not rb_f_chop. +void rb_define_global_function (const char*,void(*)(),int); +static void rb_f_chop(); +void Init_String() { + rb_define_global_function("chop", rb_f_chop, 0); +} +static void rb_f_chop() { +} + +// CHECK: call{{.*}}rb_define_global_function + +// PR10335 +typedef void (* JSErrorCallback)(void); +void js_GetErrorMessage(void); +void JS_ReportErrorNumber(JSErrorCallback errorCallback, ...); +void Interpret() { + JS_ReportErrorNumber(js_GetErrorMessage, 0); + + // CHECK: call void ({{.*}}, ...)* @JS_ReportErrorNumber({{.*}}@js_GetErrorMessage +} + + + + +// PR10337 +struct sigaction { int (*_sa_handler)(int); }; +typedef int SigHandler (); +typedef struct sigaction sighandler_cxt; +SigHandler *rl_set_sighandler(ohandler) +sighandler_cxt *ohandler; { + return 0; +} + +void rl_set_signals() { + SigHandler *oh; + oh = rl_set_sighandler(0); +} diff --git a/test/CodeGen/complex-indirect.c b/test/CodeGen/complex-indirect.c new file mode 100644 index 000000000000..45eb1954842f --- /dev/null +++ b/test/CodeGen/complex-indirect.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-apple-darwin10 | FileCheck %s + +// Make sure this doesn't crash, and that we don't generate a byval alloca +// with insufficient alignment. + +void a(int,int,int,int,int,int,__complex__ char); +void b(__complex__ char *y) { a(0,0,0,0,0,0,*y); } +// CHECK: define void @b +// CHECK: alloca { i8, i8 }*, align 8 +// CHECK: call void @a(i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, { i8, i8 }* byval align 8 diff --git a/test/CodeGen/complex.c b/test/CodeGen/complex.c index 055383ebbbe2..1212660e878d 100644 --- a/test/CodeGen/complex.c +++ b/test/CodeGen/complex.c @@ -93,3 +93,7 @@ void t6() { double t7(double _Complex c) { return __builtin_fabs(__real__(c)); } + +void t8() { + __complex__ int *x = &(__complex__ int){1}; +} diff --git a/test/CodeGen/compound-literal.c b/test/CodeGen/compound-literal.c index 4b995dbfef2e..0164c2b73862 100644 --- a/test/CodeGen/compound-literal.c +++ b/test/CodeGen/compound-literal.c @@ -1,12 +1,33 @@ -// RUN: %clang_cc1 < %s -emit-llvm +// RUN: %clang_cc1 -emit-llvm %s -o - | FileCheck %s int* a = &(int){1}; struct s {int a, b, c;} * b = &(struct s) {1, 2, 3}; // Not working; complex constants are broken // _Complex double * x = &(_Complex double){1.0f}; -int xxx() { +void xxx() { int* a = &(int){1}; struct s {int a, b, c;} * b = &(struct s) {1, 2, 3}; _Complex double * x = &(_Complex double){1.0f}; } + +// CHECK: define void @f() +void f() { + typedef struct S { int x,y; } S; + // CHECK: [[S:%[a-zA-Z0-9.]+]] = alloca [[STRUCT:%[a-zA-Z0-9.]+]], + struct S s; + // CHECK-NEXT: [[COMPOUNDLIT:%[a-zA-Z0-9.]+]] = alloca [[STRUCT]] + // CHECK-NEXT: [[CX:%[a-zA-Z0-9.]+]] = getelementptr inbounds [[STRUCT]]* [[COMPOUNDLIT]], i32 0, i32 0 + // CHECK-NEXT: [[SY:%[a-zA-Z0-9.]+]] = getelementptr inbounds [[STRUCT]]* [[S]], i32 0, i32 1 + // CHECK-NEXT: [[TMP:%[a-zA-Z0-9.]+]] = load i32* [[SY]] + // CHECK-NEXT: store i32 [[TMP]], i32* [[CX]] + // CHECK-NEXT: [[CY:%[a-zA-Z0-9.]+]] = getelementptr inbounds [[STRUCT]]* [[COMPOUNDLIT]], i32 0, i32 1 + // CHECK-NEXT: [[SX:%[a-zA-Z0-9.]+]] = getelementptr inbounds [[STRUCT]]* [[S]], i32 0, i32 0 + // CHECK-NEXT: [[TMP:%[a-zA-Z0-9.]+]] = load i32* [[SX]] + // CHECK-NEXT: store i32 [[TMP]], i32* [[CY]] + // CHECK-NEXT: [[SI8:%[a-zA-Z0-9.]+]] = bitcast [[STRUCT]]* [[S]] to i8* + // CHECK-NEXT: [[COMPOUNDLITI8:%[a-zA-Z0-9.]+]] = bitcast [[STRUCT]]* [[COMPOUNDLIT]] to i8* + // CHECK-NEXT: call void @llvm.memcpy{{.*}}(i8* [[SI8]], i8* [[COMPOUNDLITI8]] + s = (S){s.y,s.x}; + // CHECK-NEXT: ret void +} diff --git a/test/CodeGen/const-init.c b/test/CodeGen/const-init.c index c6778630a0a0..9bc3bbafdeff 100644 --- a/test/CodeGen/const-init.c +++ b/test/CodeGen/const-init.c @@ -26,21 +26,21 @@ union s2 { int g0 = (int)(&(((union s2 *) 0)->f0.f0) - 0); -// CHECK: @g1x = global {{%.}} { double 1.000000e+00{{[0]*}}, double 0.000000e+00{{[0]*}} } +// CHECK: @g1x = global { double, double } { double 1.000000e+00{{[0]*}}, double 0.000000e+00{{[0]*}} } _Complex double g1x = 1.0f; -// CHECK: @g1y = global {{%.}} { double 0.000000e+00{{[0]*}}, double 1.000000e+00{{[0]*}} } +// CHECK: @g1y = global { double, double } { double 0.000000e+00{{[0]*}}, double 1.000000e+00{{[0]*}} } _Complex double g1y = 1.0fi; -// CHECK: @g1 = global {{%.}} { i8 1, i8 10 } +// CHECK: @g1 = global { i8, i8 } { i8 1, i8 10 } _Complex char g1 = (char) 1 + (char) 10 * 1i; -// CHECK: @g2 = global %2 { i32 1, i32 10 } +// CHECK: @g2 = global { i32, i32 } { i32 1, i32 10 } _Complex int g2 = 1 + 10i; -// CHECK: @g3 = global {{%.}} { float 1.000000e+00{{[0]*}}, float 1.000000e+0{{[0]*}}1 } +// CHECK: @g3 = global { float, float } { float 1.000000e+00{{[0]*}}, float 1.000000e+0{{[0]*}}1 } _Complex float g3 = 1.0 + 10.0i; -// CHECK: @g4 = global {{%.}} { double 1.000000e+00{{[0]*}}, double 1.000000e+0{{[0]*}}1 } +// CHECK: @g4 = global { double, double } { double 1.000000e+00{{[0]*}}, double 1.000000e+0{{[0]*}}1 } _Complex double g4 = 1.0 + 10.0i; -// CHECK: @g5 = global %2 zeroinitializer +// CHECK: @g5 = global { i32, i32 } zeroinitializer _Complex int g5 = (2 + 3i) == (5 + 7i); -// CHECK: @g6 = global {{%.}} { double -1.100000e+0{{[0]*}}1, double 2.900000e+0{{[0]*}}1 } +// CHECK: @g6 = global { double, double } { double -1.100000e+0{{[0]*}}1, double 2.900000e+0{{[0]*}}1 } _Complex double g6 = (2.0 + 3.0i) * (5.0 + 7.0i); // CHECK: @g7 = global i32 1 int g7 = (2 + 3i) * (5 + 7i) == (-11 + 29i); @@ -52,14 +52,14 @@ int g9 = (2 + 3i) * (5 + 7i) != (-11 + 29i); int g10 = (2.0 + 3.0i) * (5.0 + 7.0i) != (-11.0 + 29.0i); // PR5108 -// CHECK: @gv1 = global %4 <{ i32 0, i8 7 }>, align 1 +// CHECK: @gv1 = global %struct.anon <{ i32 0, i8 7 }>, align 1 struct { unsigned long a; unsigned long b:3; } __attribute__((__packed__)) gv1 = { .a = 0x0, .b = 7, }; // PR5118 -// CHECK: @gv2 = global %5 <{ i8 1, i8* null }>, align 1 +// CHECK: @gv2 = global %struct.anon.0 <{ i8 1, i8* null }>, align 1 struct { unsigned char a; char *b; diff --git a/test/CodeGen/darwin-thread-specifier.c b/test/CodeGen/darwin-thread-specifier.c new file mode 100644 index 000000000000..605d461aca72 --- /dev/null +++ b/test/CodeGen/darwin-thread-specifier.c @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 -triple x86_64-apple-macosx10.7.0 -emit-llvm -o - %s | FileCheck %s +// CHECK: @b = thread_local global i32 5, align 4 +__thread int b = 5; diff --git a/test/CodeGen/debug-info-iv.c b/test/CodeGen/debug-info-iv.c new file mode 100644 index 000000000000..92654738cfc5 --- /dev/null +++ b/test/CodeGen/debug-info-iv.c @@ -0,0 +1,35 @@ +// RUN: %clang_cc1 -triple x86_64-darwin-apple -Os -S -g -o - %s | FileCheck %s + +int calculate(int); +static void test_indvars(int *Array1, int Array2[100][200]) { + unsigned i, j; + Array1[1] = Array2[3][6] = 12345; + + for (i = 0; i < 100; i+=2) + Array1[i] = i; /* Step by non unit amount */ + + for (i = 3; i < 103; i++) + Array1[i] = i+4; /* Step with an offset */ + + for (i = 13; i < 100; i++) + for (j = 0; j < 100; j+=3) /* 2d array access */ + Array2[i][j/3] = Array2[i][i]; +} + + +int main() { + int Array[100][200], i, j; + double sum = 0.0; + + for (i=0; i < 100; i+=2) + for (j=0; j < 200; j++) + Array[i][j] = 0; + test_indvars(Array[0], Array); + +//CHECK: .loc 2 30 3 + for (i=0; i < 100; i+=2) + for (j=0; j < 200; j++) + sum += Array[i][j]; + + return calculate(sum); +} diff --git a/test/CodeGen/debug-info-member.c b/test/CodeGen/debug-info-member.c new file mode 100644 index 000000000000..54066fa53060 --- /dev/null +++ b/test/CodeGen/debug-info-member.c @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 -emit-llvm -g < %s | grep DW_TAG_member | grep \!3 + +struct A { int x; } a; diff --git a/test/CodeGen/debug-info.c b/test/CodeGen/debug-info.c index 876c6c22423d..af2ce969bceb 100644 --- a/test/CodeGen/debug-info.c +++ b/test/CodeGen/debug-info.c @@ -54,3 +54,8 @@ __uint128_t foo128 () __uint128_t int128 = 44; return int128; } + +// CHECK: uint64x2_t +typedef unsigned long long uint64_t; +typedef uint64_t uint64x2_t __attribute__((ext_vector_type(2))); +uint64x2_t extvectbar[4]; diff --git a/test/CodeGen/decl.c b/test/CodeGen/decl.c index 29520d77ff48..21b75797f0c3 100644 --- a/test/CodeGen/decl.c +++ b/test/CodeGen/decl.c @@ -2,8 +2,8 @@ // CHECK: @test1.x = internal constant [12 x i32] [i32 1 // CHECK: @test2.x = internal unnamed_addr constant [13 x i32] [i32 1, -// CHECK: @test5w = global %0 { i32 2, [4 x i8] undef } -// CHECK: @test5y = global %union.test5u { double 7.300000e+0{{[0]*}}1 } +// CHECK: @test5w = global { i32, [4 x i8] } { i32 2, [4 x i8] undef } +// CHECK: @test5y = global { double } { double 7.300000e+0{{[0]*}}1 } // CHECK: @test6.x = internal unnamed_addr constant %struct.SelectDest { i8 1, i8 2, i32 3, i32 0 } diff --git a/test/CodeGen/designated-initializers.c b/test/CodeGen/designated-initializers.c index d928296ef230..6561ce56d823 100644 --- a/test/CodeGen/designated-initializers.c +++ b/test/CodeGen/designated-initializers.c @@ -8,10 +8,10 @@ struct foo { // CHECK: @u = global %union.anon zeroinitializer union { int i; float f; } u = { }; -// CHECK: @u2 = global %1 { i32 0, [4 x i8] undef } +// CHECK: @u2 = global { i32, [4 x i8] } { i32 0, [4 x i8] undef } union { int i; double f; } u2 = { }; -// CHECK: @u3 = global %2 zeroinitializer +// CHECK: @u3 = global %union.anon.1 zeroinitializer union { double f; int i; } u3 = { }; // CHECK: @b = global [2 x i32] [i32 0, i32 22] @@ -39,11 +39,11 @@ struct ds ds0 = { { { .a = 0 } } }; struct ds ds1 = { { .a = 1 } }; struct ds ds2 = { { .b = 1 } }; struct ds ds3 = { .a = 0 }; -// CHECK: @ds4 = global %3 { %4 { %struct.anon zeroinitializer, i16 0, %struct.anon { i16 1 } } } +// CHECK: @ds4 = global %struct.ds { %struct.anon.3 { %struct.anon zeroinitializer, i16 0, %struct.anon.2 { i16 1 } } } struct ds ds4 = { .c = 1 }; struct ds ds5 = { { { .a = 0 } }, .b = 1 }; struct ds ds6 = { { .a = 0, .b = 1 } }; -// CHECK: @ds7 = global %3 { %4 { %struct.anon { i16 2 }, i16 3, %struct.anon zeroinitializer } } +// CHECK: @ds7 = global %struct.ds { %struct.anon.3 { %struct.anon { i16 2 }, i16 3, %struct.anon.2 zeroinitializer } } struct ds ds7 = { { { .a = 1 @@ -59,7 +59,7 @@ void test1(int argc, char **argv) .b = 1024, }; - // CHECK: bitcast %union.anon* %u2 + // CHECK: bitcast %union.anon.4* %u2 // CHECK: call void @llvm.memset union { int i; float f; } u2 = { }; diff --git a/test/CodeGen/flexible-array-init.c b/test/CodeGen/flexible-array-init.c index 36323502a415..d3079dce3ddc 100644 --- a/test/CodeGen/flexible-array-init.c +++ b/test/CodeGen/flexible-array-init.c @@ -1,7 +1,7 @@ // RUN: %clang_cc1 -triple i386-unknown-unknown -emit-llvm -o - %s | FileCheck %s struct { int x; int y[]; } a = { 1, 7, 11 }; -// CHECK: @a = global %0 { i32 1, [2 x i32] [i32 7, i32 11] } +// CHECK: @a = global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 7, i32 11] } struct { int x; int y[]; } b = { 1, { 13, 15 } }; -// CHECK: @b = global %0 { i32 1, [2 x i32] [i32 13, i32 15] } +// CHECK: @b = global { i32, [2 x i32] } { i32 1, [2 x i32] [i32 13, i32 15] } diff --git a/test/CodeGen/functions.c b/test/CodeGen/functions.c index a2c692d0ce3e..e51f93e57411 100644 --- a/test/CodeGen/functions.c +++ b/test/CodeGen/functions.c @@ -59,3 +59,9 @@ void f8_test() { // CHECK: declare void @f8_user({{.*}}*) // CHECK: declare void @f8_callback() } + +// PR10204: don't crash +static void test9_helper(void) {} +void test9() { + (void) test9_helper; +} diff --git a/test/CodeGen/global-init.c b/test/CodeGen/global-init.c index 351ca9e35ac7..074c2a065a86 100644 --- a/test/CodeGen/global-init.c +++ b/test/CodeGen/global-init.c @@ -27,12 +27,12 @@ struct ManyFields { int f; }; -// CHECK: global %0 { i32 1, i32 2, i32 0, i8 0, i32 0, i32 0 } +// CHECK: global %struct.ManyFields { i32 1, i32 2, i32 0, i8 0, i32 0, i32 0 } struct ManyFields FewInits = {1, 2}; // PR6766 -// CHECK: @l = global %1 { [24 x i8] c"f\00\00\00o\00\00\00o\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", i32 1 } +// CHECK: @l = global { [24 x i8], i32 } { [24 x i8] c"f\00\00\00o\00\00\00o\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00", i32 1 } typedef __WCHAR_TYPE__ wchar_t; struct K { wchar_t L[6]; diff --git a/test/CodeGen/init.c b/test/CodeGen/init.c index 0f94729a949e..599b4f23dbdc 100644 --- a/test/CodeGen/init.c +++ b/test/CodeGen/init.c @@ -115,3 +115,11 @@ void test11(struct test11S *P) { // CHECK: store i32 4 // CHECK: ret void } + + +// Verify that we can convert a recursive struct with a memory that returns +// an instance of the struct we're converting. +struct test12 { + struct test12 (*p)(void); +} test12g; + diff --git a/test/CodeGen/libcalls.c b/test/CodeGen/libcalls.c index 828d7de6cb08..5ff684fd5b66 100644 --- a/test/CodeGen/libcalls.c +++ b/test/CodeGen/libcalls.c @@ -50,3 +50,26 @@ void test_pow(float a0, double a1, long double a2) { // CHECK-NO: declare float @llvm.pow.f32(float, float) nounwind readonly // CHECK-NO: declare double @llvm.pow.f64(double, double) nounwind readonly // CHECK-NO: declare x86_fp80 @llvm.pow.f80(x86_fp80, x86_fp80) nounwind readonly + +// CHECK-YES: define void @test_fma +// CHECK-NO: define void @test_fma +void test_fma(float a0, double a1, long double a2) { + // CHECK-YES: call float @llvm.fma.f32 + // CHECK-NO: call float @llvm.fma.f32 + float l0 = fmaf(a0, a0, a0); + + // CHECK-YES: call double @llvm.fma.f64 + // CHECK-NO: call double @llvm.fma.f64 + double l1 = fma(a1, a1, a1); + + // CHECK-YES: call x86_fp80 @llvm.fma.f80 + // CHECK-NO: call x86_fp80 @llvm.fma.f80 + long double l2 = fmal(a2, a2, a2); +} + +// CHECK-YES: declare float @llvm.fma.f32(float, float, float) nounwind readnone +// CHECK-YES: declare double @llvm.fma.f64(double, double, double) nounwind readnone +// CHECK-YES: declare x86_fp80 @llvm.fma.f80(x86_fp80, x86_fp80, x86_fp80) nounwind readnone +// CHECK-NO: declare float @llvm.fma.f32(float, float, float) nounwind readnone +// CHECK-NO: declare double @llvm.fma.f64(double, double, double) nounwind readnone +// CHECK-NO: declare x86_fp80 @llvm.fma.f80(x86_fp80, x86_fp80, x86_fp80) nounwind readnone diff --git a/test/CodeGen/mmx-inline-asm.c b/test/CodeGen/mmx-inline-asm.c index c473a930ecc8..5d1371ed90a1 100644 --- a/test/CodeGen/mmx-inline-asm.c +++ b/test/CodeGen/mmx-inline-asm.c @@ -2,7 +2,7 @@ // <rdar://problem/9091220> #include <mmintrin.h> -// CHECK: type { x86_mmx, x86_mmx, x86_mmx, x86_mmx, x86_mmx, x86_mmx, x86_mmx } +// CHECK: { x86_mmx, x86_mmx, x86_mmx, x86_mmx, x86_mmx, x86_mmx, x86_mmx } void foo(long long fill) { __m64 vfill = _mm_cvtsi64_m64(fill); diff --git a/test/CodeGen/ms-anonymous-struct.c b/test/CodeGen/ms-anonymous-struct.c index 3afe440cafe7..b41caab0309e 100644 --- a/test/CodeGen/ms-anonymous-struct.c +++ b/test/CodeGen/ms-anonymous-struct.c @@ -1,19 +1,19 @@ // RUN: %clang_cc1 -fms-extensions -emit-llvm -o - %s | FileCheck %s +// CHECK: %struct.test = type { i32, %struct.nested2, i32 } +// CHECK: %struct.nested2 = type { i32, %struct.nested1, i32 } // CHECK: %struct.nested1 = type { i32, i32 } typedef struct nested1 { int a1; int b1; } NESTED1; -// CHECK: %struct.nested2 = type { i32, %struct.nested1, i32 } struct nested2 { int a; NESTED1; int b; }; -// CHECK: %struct.test = type { i32, %struct.nested2, i32 } struct test { int x; struct nested2; diff --git a/test/CodeGen/packed-union.c b/test/CodeGen/packed-union.c index 0aeed008b752..31ce61440781 100644 --- a/test/CodeGen/packed-union.c +++ b/test/CodeGen/packed-union.c @@ -1,6 +1,5 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm %s -o %t -// RUN: grep "struct._attrs = type <{ i32, i8 }>" %t typedef struct _attrs { unsigned file_attributes; unsigned char filename_length; diff --git a/test/CodeGen/pragma-pack-3.c b/test/CodeGen/pragma-pack-3.c index 676f0d77eba3..04b636e9c774 100644 --- a/test/CodeGen/pragma-pack-3.c +++ b/test/CodeGen/pragma-pack-3.c @@ -1,9 +1,7 @@ // RUN: %clang_cc1 -triple i386-apple-darwin9 %s -emit-llvm -o - | FileCheck -check-prefix X32 %s -// CHECK-X32: %struct.menu = type <{ i8*, i8, i8 }> // CHECK-X32: %union.command = type <{ i8*, [2 x i8] }> // RUN: %clang_cc1 -triple x86_64-apple-darwin9 %s -emit-llvm -o - | FileCheck -check-prefix X64 %s -// CHECK-X64: %struct.menu = type <{ i8*, i8, i8 }> // CHECK-X64: %union.command = type <{ i8*, [2 x i8] }> // <rdar://problem/7184250> diff --git a/test/CodeGen/private-extern-redef.c b/test/CodeGen/private-extern-redef.c new file mode 100644 index 000000000000..580ce9bd4a08 --- /dev/null +++ b/test/CodeGen/private-extern-redef.c @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple x86_64-darwin-apple -emit-llvm -o - %s | FileCheck %s +// rdar://9609649 + +__private_extern__ const int I; +__private_extern__ const int J = 927; + +__private_extern__ const int K; +const int K = 37; + +const int L = 10; +__private_extern__ const int L; + +__private_extern__ int M; +int M = 20; + +__private_extern__ int N; +int N; + +__private_extern__ int O; +int O=1; + +__private_extern__ int P; +extern int P; + +void bar(int); + +void foo() { + bar(I); +} + +// CHECK: @J = hidden constant +// CHECK: @K = hidden constant +// CHECK: @L = constant +// CHECK: @M = hidden global +// CHECK: @O = hidden global +// CHECK: @I = external hidden +// CHECK: @N = common hidden global +// CHECK-NOT: @P + diff --git a/test/CodeGen/struct-init.c b/test/CodeGen/struct-init.c index 861c41e59cc3..8a605c1b67c8 100644 --- a/test/CodeGen/struct-init.c +++ b/test/CodeGen/struct-init.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -emit-llvm-only +// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s typedef struct _zend_ini_entry zend_ini_entry; struct _zend_ini_entry { @@ -29,3 +29,11 @@ typedef struct __simd64_uint32_t { void foo() { const uint32x2_t signBit = { (uint2) 0x80000000 }; } + +// CHECK: %struct.fp_struct_foo = type { void (i32)* } +struct fp_struct_bar { int a; }; + +struct fp_struct_foo { + void (*FP)(struct fp_struct_bar); +} G; + diff --git a/test/CodeGen/struct.c b/test/CodeGen/struct.c index 25477a052e8a..e1739314d7fc 100644 --- a/test/CodeGen/struct.c +++ b/test/CodeGen/struct.c @@ -181,3 +181,16 @@ range f18() { rangepair rp; return (rp = f18_ext()).range1; } + + + +// Complex forward reference of struct. +struct f19S; +extern struct f19T { + struct f19S (*p)(void); +} t; +struct f19S { int i; }; +void f19(void) { + t.p(); +} + diff --git a/test/CodeGen/trapv.c b/test/CodeGen/trapv.c index 7f192c634c88..f52dad556485 100644 --- a/test/CodeGen/trapv.c +++ b/test/CodeGen/trapv.c @@ -1,7 +1,5 @@ // RUN: %clang_cc1 -triple x86_64-apple-darwin10 -ftrapv %s -emit-llvm -o - | FileCheck %s -// CHECK: [[I32O:%.*]] = type { i32, i1 } - unsigned int ui, uj, uk; int i, j, k; @@ -16,9 +14,9 @@ void test0() { // CHECK: [[T1:%.*]] = load i32* @j // CHECK-NEXT: [[T2:%.*]] = load i32* @k - // CHECK-NEXT: [[T3:%.*]] = call [[I32O]] @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 [[T2]]) - // CHECK-NEXT: [[T4:%.*]] = extractvalue [[I32O]] [[T3]], 0 - // CHECK-NEXT: [[T5:%.*]] = extractvalue [[I32O]] [[T3]], 1 + // CHECK-NEXT: [[T3:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T3]], 0 + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T3]], 1 // CHECK-NEXT: br i1 [[T5]] // CHECK: call void @llvm.trap() i = j + k; @@ -30,9 +28,9 @@ void test1() { opaque(i++); // CHECK: [[T1:%.*]] = load i32* @i - // CHECK-NEXT: [[T2:%.*]] = call [[I32O]] @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 1) - // CHECK-NEXT: [[T3:%.*]] = extractvalue [[I32O]] [[T2]], 0 - // CHECK-NEXT: [[T4:%.*]] = extractvalue [[I32O]] [[T2]], 1 + // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 1) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0 + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T2]], 1 // CHECK-NEXT: br i1 [[T4]] // CHECK: call void @llvm.trap() } @@ -43,9 +41,9 @@ void test2() { opaque(++i); // CHECK: [[T1:%.*]] = load i32* @i - // CHECK-NEXT: [[T2:%.*]] = call [[I32O]] @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 1) - // CHECK-NEXT: [[T3:%.*]] = extractvalue [[I32O]] [[T2]], 0 - // CHECK-NEXT: [[T4:%.*]] = extractvalue [[I32O]] [[T2]], 1 + // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.sadd.with.overflow.i32(i32 [[T1]], i32 1) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 0 + // CHECK-NEXT: [[T4:%.*]] = extractvalue { i32, i1 } [[T2]], 1 // CHECK-NEXT: br i1 [[T4]] // CHECK: call void @llvm.trap() } diff --git a/test/CodeGen/union-init2.c b/test/CodeGen/union-init2.c index 1386c27cc0bd..bf20696a22c1 100644 --- a/test/CodeGen/union-init2.c +++ b/test/CodeGen/union-init2.c @@ -1,11 +1,11 @@ // RUN: %clang_cc1 -emit-llvm %s -o - -triple i686-pc-linux-gnu | FileCheck %s // Make sure we generate something sane instead of a ptrtoint -// CHECK: bitcast (%0* @r to %union.x*), [4 x i8] undef +// CHECK: bitcast ({ %union.x*, [4 x i8] }* @r to %union.x*), [4 x i8] undef union x {long long b;union x* a;} r = {.a = &r}; -// CHECK: global %1 { [3 x i8] zeroinitializer, [5 x i8] undef } +// CHECK: global { [3 x i8], [5 x i8] } { [3 x i8] zeroinitializer, [5 x i8] undef } union z { char a[3]; long long b; diff --git a/test/CodeGen/vla.c b/test/CodeGen/vla.c index e5114a5b0db9..c9612bc16032 100644 --- a/test/CodeGen/vla.c +++ b/test/CodeGen/vla.c @@ -88,13 +88,60 @@ int test2(int n) // http://llvm.org/PR8567 // CHECK: define double @test_PR8567 double test_PR8567(int n, double (*p)[n][5]) { - // CHECK: store [[vla_type:.*]] %p, - // CHECK: load i32* - // CHECK-NEXT: mul i32 40 - // CHECK-NEXT: [[byte_idx:%.*]] = mul i32 1 - // CHECK-NEXT: [[tmp_1:%.*]] = load [[vla_type]]* - // CHECK-NEXT: [[tmp_2:%.*]] = bitcast [[vla_type]] [[tmp_1]] to i8* - // CHECK-NEXT: [[idx:%.*]] = getelementptr inbounds i8* [[tmp_2]], i32 [[byte_idx]] - // CHECK-NEXT: bitcast i8* [[idx]] to [[vla_type]] + // CHECK: [[NV:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[PV:%.*]] = alloca [5 x double]*, align 4 + // CHECK-NEXT: store + // CHECK-NEXT: store + // CHECK-NEXT: [[N:%.*]] = load i32* [[NV]], align 4 + // CHECK-NEXT: [[P:%.*]] = load [5 x double]** [[PV]], align 4 + // CHECK-NEXT: [[T0:%.*]] = mul nsw i32 1, [[N]] + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [5 x double]* [[P]], i32 [[T0]] + // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds [5 x double]* [[T1]], i32 2 + // CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds [5 x double]* [[T2]], i32 0, i32 3 + // CHECK-NEXT: [[T4:%.*]] = load double* [[T3]] + // CHECK-NEXT: ret double [[T4]] return p[1][2][3]; } + +int test4(unsigned n, char (*p)[n][n+1][6]) { + // CHECK: define i32 @test4( + // CHECK: [[N:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[P:%.*]] = alloca [6 x i8]*, align 4 + // CHECK-NEXT: [[P2:%.*]] = alloca [6 x i8]*, align 4 + // CHECK-NEXT: store i32 + // CHECK-NEXT: store [6 x i8]* + + // VLA captures. + // CHECK-NEXT: [[DIM0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[DIM1:%.*]] = add i32 [[T0]], 1 + + // __typeof. FIXME: does this really need to be loaded? + // CHECK-NEXT: load [6 x i8]** [[P]] + + // CHECK-NEXT: [[T0:%.*]] = load [6 x i8]** [[P]], align 4 + // CHECK-NEXT: [[T1:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[T2:%.*]] = udiv i32 [[T1]], 2 + // CHECK-NEXT: [[T3:%.*]] = mul nuw i32 [[DIM0]], [[DIM1]] + // CHECK-NEXT: [[T4:%.*]] = mul nsw i32 [[T2]], [[T3]] + // CHECK-NEXT: [[T5:%.*]] = getelementptr inbounds [6 x i8]* [[T0]], i32 [[T4]] + // CHECK-NEXT: [[T6:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[T7:%.*]] = udiv i32 [[T6]], 4 + // CHECK-NEXT: [[T8:%.*]] = sub i32 0, [[T7]] + // CHECK-NEXT: [[T9:%.*]] = mul nuw i32 [[DIM0]], [[DIM1]] + // CHECK-NEXT: [[T10:%.*]] = mul nsw i32 [[T8]], [[T9]] + // CHECK-NEXT: [[T11:%.*]] = getelementptr inbounds [6 x i8]* [[T5]], i32 [[T10]] + // CHECK-NEXT: store [6 x i8]* [[T11]], [6 x i8]** [[P2]], align 4 + __typeof(p) p2 = (p + n/2) - n/4; + + // CHECK-NEXT: [[T0:%.*]] = load [6 x i8]** [[P2]], align 4 + // CHECK-NEXT: [[T1:%.*]] = load [6 x i8]** [[P]], align 4 + // CHECK-NEXT: [[T2:%.*]] = ptrtoint [6 x i8]* [[T0]] to i32 + // CHECK-NEXT: [[T3:%.*]] = ptrtoint [6 x i8]* [[T1]] to i32 + // CHECK-NEXT: [[T4:%.*]] = sub i32 [[T2]], [[T3]] + // CHECK-NEXT: [[T5:%.*]] = mul nuw i32 [[DIM0]], [[DIM1]] + // CHECK-NEXT: [[T6:%.*]] = mul nuw i32 6, [[T5]] + // CHECK-NEXT: [[T7:%.*]] = sdiv exact i32 [[T4]], [[T6]] + // CHECK-NEXT: ret i32 [[T7]] + return p2 - p; +} diff --git a/test/CodeGen/volatile-1.c b/test/CodeGen/volatile-1.c index f54afffdadab..7c7a822e880c 100644 --- a/test/CodeGen/volatile-1.c +++ b/test/CodeGen/volatile-1.c @@ -4,7 +4,7 @@ volatile int i, j, k; volatile int ar[5]; volatile char c; -// CHECK: @ci = common global [[CINT:%.*]] zeroinitializer +// CHECK: @ci = common global [[CINT:.*]] zeroinitializer volatile _Complex int ci; volatile struct S { #ifdef __cplusplus diff --git a/test/CodeGen/volatile-2.c b/test/CodeGen/volatile-2.c index 1ceaf17dfcb4..490b7d776b9f 100644 --- a/test/CodeGen/volatile-2.c +++ b/test/CodeGen/volatile-2.c @@ -3,8 +3,8 @@ void test0() { // CHECK: define void @test0() // CHECK: [[F:%.*]] = alloca float - // CHECK-NEXT: [[REAL:%.*]] = volatile load float* getelementptr inbounds ({{%.*}} @test0_v, i32 0, i32 0) - // CHECK-NEXT: volatile load float* getelementptr inbounds ({{%.*}} @test0_v, i32 0, i32 1) + // CHECK-NEXT: [[REAL:%.*]] = volatile load float* getelementptr inbounds ({ float, float }* @test0_v, i32 0, i32 0) + // CHECK-NEXT: volatile load float* getelementptr inbounds ({{.*}} @test0_v, i32 0, i32 1) // CHECK-NEXT: store float [[REAL]], float* [[F]], align 4 // CHECK-NEXT: ret void extern volatile _Complex float test0_v; @@ -13,10 +13,10 @@ void test0() { void test1() { // CHECK: define void @test1() - // CHECK: [[REAL:%.*]] = volatile load float* getelementptr inbounds ({{%.*}} @test1_v, i32 0, i32 0) - // CHECK-NEXT: [[IMAG:%.*]] = volatile load float* getelementptr inbounds ({{%.*}} @test1_v, i32 0, i32 1) - // CHECK-NEXT: volatile store float [[REAL]], float* getelementptr inbounds ({{%.*}} @test1_v, i32 0, i32 0) - // CHECK-NEXT: volatile store float [[IMAG]], float* getelementptr inbounds ({{%.*}} @test1_v, i32 0, i32 1) + // CHECK: [[REAL:%.*]] = volatile load float* getelementptr inbounds ({{.*}} @test1_v, i32 0, i32 0) + // CHECK-NEXT: [[IMAG:%.*]] = volatile load float* getelementptr inbounds ({{.*}} @test1_v, i32 0, i32 1) + // CHECK-NEXT: volatile store float [[REAL]], float* getelementptr inbounds ({{.*}} @test1_v, i32 0, i32 0) + // CHECK-NEXT: volatile store float [[IMAG]], float* getelementptr inbounds ({{.*}} @test1_v, i32 0, i32 1) // CHECK-NEXT: ret void extern volatile _Complex float test1_v; test1_v = test1_v; diff --git a/test/CodeGen/x86_32-arguments-darwin.c b/test/CodeGen/x86_32-arguments-darwin.c index f7e2a5368de2..731d4f613f26 100644 --- a/test/CodeGen/x86_32-arguments-darwin.c +++ b/test/CodeGen/x86_32-arguments-darwin.c @@ -1,5 +1,4 @@ -// RUN: %clang_cc1 -w -fblocks -triple i386-apple-darwin9 -emit-llvm -o %t %s -// RUN: FileCheck < %t %s +// RUN: %clang_cc1 -w -fblocks -triple i386-apple-darwin9 -target-cpu yonah -emit-llvm -o - %s | FileCheck %s // CHECK: define signext i8 @f0() char f0(void) { @@ -232,7 +231,7 @@ v4i32 f55(v4i32 arg) { return arg+arg; } // CHECK: i8 signext %a0, %struct.s56_0* byval align 4 %a1, // CHECK: x86_mmx %a2.coerce, %struct.s56_1* byval align 4, // CHECK: i64 %a4.coerce, %struct.s56_2* byval align 4, -// CHECK: <4 x i32> %a6, %struct.s39* byval align 16 %a7, +// CHECK: <4 x i32> %a6, %struct.s56_3* byval align 16 %a7, // CHECK: <2 x double> %a8, %struct.s56_4* byval align 16 %a9, // CHECK: <8 x i32> %a10, %struct.s56_5* byval align 4, // CHECK: <4 x double> %a12, %struct.s56_6* byval align 4) @@ -241,7 +240,7 @@ v4i32 f55(v4i32 arg) { return arg+arg; } // CHECK: i32 %{{[^ ]*}}, %struct.s56_0* byval align 4 %{{[^ ]*}}, // CHECK: x86_mmx %{{[^ ]*}}, %struct.s56_1* byval align 4 %{{[^ ]*}}, // CHECK: i64 %{{[^ ]*}}, %struct.s56_2* byval align 4 %{{[^ ]*}}, -// CHECK: <4 x i32> %{{[^ ]*}}, %struct.s39* byval align 16 %{{[^ ]*}}, +// CHECK: <4 x i32> %{{[^ ]*}}, %struct.s56_3* byval align 16 %{{[^ ]*}}, // CHECK: <2 x double> %{{[^ ]*}}, %struct.s56_4* byval align 16 %{{[^ ]*}}, // CHECK: <8 x i32> {{[^ ]*}}, %struct.s56_5* byval align 4 %{{[^ ]*}}, // CHECK: <4 x double> {{[^ ]*}}, %struct.s56_6* byval align 4 %{{[^ ]*}}) diff --git a/test/CodeGen/x86_32-arguments-linux.c b/test/CodeGen/x86_32-arguments-linux.c index 2f246f85b519..81dcaf6af571 100644 --- a/test/CodeGen/x86_32-arguments-linux.c +++ b/test/CodeGen/x86_32-arguments-linux.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -w -fblocks -triple i386-pc-linux-gnu -emit-llvm -o %t %s +// RUN: %clang_cc1 -w -fblocks -triple i386-pc-linux-gnu -target-cpu pentium4 -emit-llvm -o %t %s // RUN: FileCheck < %t %s // CHECK: define void @f56( diff --git a/test/CodeGen/x86_32-arguments-nommx.c b/test/CodeGen/x86_32-arguments-nommx.c new file mode 100644 index 000000000000..40362f796f6c --- /dev/null +++ b/test/CodeGen/x86_32-arguments-nommx.c @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -target-feature -mmx -target-feature +sse2 -triple i686-pc-linux-gnu -emit-llvm -o - %s | FileCheck %s + +// no-mmx should put mmx into memory +typedef int __attribute__((vector_size (8))) i32v2; +int a(i32v2 x) { return x[0]; } +// CHECK: define i32 @a(i64 %x.coerce) + +// but SSE2 vectors should still go into an SSE2 register +typedef int __attribute__((vector_size (16))) i32v4; +int b(i32v4 x) { return x[0]; } +// CHECK: define i32 @b(<4 x i32> %x) diff --git a/test/CodeGen/x86_64-arguments.c b/test/CodeGen/x86_64-arguments.c index 75c4788f7120..221c7d38a73d 100644 --- a/test/CodeGen/x86_64-arguments.c +++ b/test/CodeGen/x86_64-arguments.c @@ -42,8 +42,8 @@ void f7(e7 a0) { // Test merging/passing of upper eightbyte with X87 class. // -// CHECK: define void @f8_1(%struct.s19* sret %agg.result) -// CHECK: define void @f8_2(%struct.s19* byval align 16 %a0) +// CHECK: define void @f8_1(%union.u8* sret %agg.result) +// CHECK: define void @f8_2(%union.u8* byval align 16 %a0) union u8 { long double a; int b; @@ -58,7 +58,7 @@ struct s9 { int a; int b; int : 0; } f9(void) { while (1) {} } struct s10 { int a; int b; int : 0; }; void f10(struct s10 a0) {} -// CHECK: define void @f11(%struct.s19* sret %agg.result) +// CHECK: define void @f11(%union.anon* sret %agg.result) union { long double a; float b; } f11() { while (1) {} } // CHECK: define i32 @f12_0() @@ -147,7 +147,7 @@ struct f24s { long a; int b; }; struct f23S f24(struct f23S *X, struct f24s *P2) { return *X; - // CHECK: define %struct.f24s @f24(%struct.f23S* %X, %struct.f24s* %P2) + // CHECK: define { i64, i32 } @f24(%struct.f23S* %X, %struct.f24s* %P2) } // rdar://8248065 @@ -169,7 +169,7 @@ struct foo26 { }; struct foo26 f26(struct foo26 *P) { - // CHECK: define %struct.foo26 @f26(%struct.foo26* %P) + // CHECK: define { i32*, float* } @f26(%struct.foo26* %P) return *P; } @@ -259,3 +259,48 @@ void f9122143() func(ss); } +// CHECK: define double @f36(double %arg.coerce) +typedef unsigned v2i32 __attribute((__vector_size__(8))); +v2i32 f36(v2i32 arg) { return arg; } + +// CHECK: declare void @f38(<8 x float>) +// CHECK: declare void @f37(<8 x float>) +typedef float __m256 __attribute__ ((__vector_size__ (32))); +typedef struct { + __m256 m; +} s256; + +s256 x38; +__m256 x37; + +void f38(s256 x); +void f37(__m256 x); +void f39() { f38(x38); f37(x37); } + +// The two next tests make sure that the struct below is passed +// in the same way regardless of avx being used + +// CHECK: declare void @func40(%struct.t128* byval align 16) +typedef float __m128 __attribute__ ((__vector_size__ (16))); +typedef struct t128 { + __m128 m; + __m128 n; +} two128; + +extern void func40(two128 s); +void func41(two128 s) { + func40(s); +} + +// CHECK: declare void @func42(%struct.t128_2* byval align 16) +typedef struct xxx { + __m128 array[2]; +} Atwo128; +typedef struct t128_2 { + Atwo128 x; +} SA; + +extern void func42(SA s); +void func43(SA s) { + func42(s); +} diff --git a/test/CodeGenCXX/anonymous-union-member-initializer.cpp b/test/CodeGenCXX/anonymous-union-member-initializer.cpp index 2ddafecf6651..324ff4aee2b7 100644 --- a/test/CodeGenCXX/anonymous-union-member-initializer.cpp +++ b/test/CodeGenCXX/anonymous-union-member-initializer.cpp @@ -86,7 +86,7 @@ namespace test3 { // CHECK-NEXT: [[UNION:%.*]] = getelementptr inbounds {{.*}} [[THIS]], i32 0, i32 0 // CHECK-NEXT: [[STRUCT:%.*]] = bitcast {{.*}}* [[UNION]] to // CHECK-NEXT: [[CALLBACK:%.*]] = getelementptr inbounds {{.*}} [[STRUCT]], i32 0, i32 0 - // CHECK-NEXT: store void (i8*)* null, void (i8*)** [[CALLBACK]] + // CHECK: store // CHECK-NEXT: [[UNION:%.*]] = getelementptr inbounds {{.*}} [[THIS]], i32 0, i32 0 // CHECK-NEXT: [[STRUCT:%.*]] = bitcast {{.*}}* [[UNION]] to // CHECK-NEXT: [[CVALUE:%.*]] = getelementptr inbounds {{.*}} [[STRUCT]], i32 0, i32 1 @@ -114,3 +114,19 @@ template <typename T> struct Foo { }; }; Foo<int> f; + +namespace PR9683 { + struct QueueEntry { + union { + struct { + void* mPtr; + union { + unsigned mSubmissionTag; + }; + }; + unsigned mValue; + }; + QueueEntry() {} + }; + QueueEntry QE; +} diff --git a/test/CodeGenCXX/attr-used.cpp b/test/CodeGenCXX/attr-used.cpp index 26109e7c6300..2c82184081b9 100644 --- a/test/CodeGenCXX/attr-used.cpp +++ b/test/CodeGenCXX/attr-used.cpp @@ -2,8 +2,8 @@ // <rdar://problem/8684363>: clang++ not respecting __attribute__((used)) on destructors struct X0 { - // CHECK: define linkonce_odr void @_ZN2X0C1Ev + // CHECK: define linkonce_odr {{.*}} @_ZN2X0C1Ev __attribute__((used)) X0() {} - // CHECK: define linkonce_odr void @_ZN2X0D1Ev + // CHECK: define linkonce_odr {{.*}} @_ZN2X0D1Ev __attribute__((used)) ~X0() {} }; diff --git a/test/CodeGenCXX/block-byref-cxx-objc.cpp b/test/CodeGenCXX/block-byref-cxx-objc.cpp index a4fbd6cb6233..135e0c755194 100644 --- a/test/CodeGenCXX/block-byref-cxx-objc.cpp +++ b/test/CodeGenCXX/block-byref-cxx-objc.cpp @@ -16,9 +16,9 @@ int main() } // CHECK: define internal void @__Block_byref_object_copy_ -// CHECK: call void @_ZN1AC1ERKS_ +// CHECK: call {{.*}} @_ZN1AC1ERKS_ // CHECK: define internal void @__Block_byref_object_dispose_ -// CHECK: call void @_ZN1AD1Ev +// CHECK: call {{.*}} @_ZN1AD1Ev // CHECK: define internal void @__copy_helper_block_ // CHECK: call void @_Block_object_assign // CHECK: define internal void @__destroy_helper_block_ diff --git a/test/CodeGenCXX/blocks.cpp b/test/CodeGenCXX/blocks.cpp index a4d5b86565e2..0e310bdbbc13 100644 --- a/test/CodeGenCXX/blocks.cpp +++ b/test/CodeGenCXX/blocks.cpp @@ -31,7 +31,7 @@ namespace test1 { // ...unless they have mutable fields... // CHECK: define void @_ZN5test15test3Ev() - // CHECK: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], // CHECK: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()* // CHECK: store void ()* [[T0]], void ()** @out struct mut { mutable int x; }; @@ -43,7 +43,7 @@ namespace test1 { // ...or non-trivial destructors... // CHECK: define void @_ZN5test15test4Ev() // CHECK: [[OBJ:%.*]] = alloca - // CHECK: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], // CHECK: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to void ()* // CHECK: store void ()* [[T0]], void ()** @out struct scope { int x; ~scope(); }; diff --git a/test/CodeGenCXX/class-layout.cpp b/test/CodeGenCXX/class-layout.cpp index 96fbae8970e0..9569f476dd87 100644 --- a/test/CodeGenCXX/class-layout.cpp +++ b/test/CodeGenCXX/class-layout.cpp @@ -20,8 +20,8 @@ namespace Test3 { namespace Test4 { // Test from PR5589. - // CHECK: %"struct.Test4::A" = type { i32, i8, float } // CHECK: %"struct.Test4::B" = type { %"struct.Test4::A", i16, double } + // CHECK: %"struct.Test4::A" = type { i32, i8, float } struct A { int a; char c; diff --git a/test/CodeGenCXX/compound-literals.cpp b/test/CodeGenCXX/compound-literals.cpp new file mode 100644 index 000000000000..cd44e97c6744 --- /dev/null +++ b/test/CodeGenCXX/compound-literals.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +struct X { + X(); + X(const X&); + X(const char*); + ~X(); +}; + +struct Y { + int i; + X x; +}; + +// CHECK: define i32 @_Z1fv() +int f() { + // CHECK: [[LVALUE:%[a-z0-9.]+]] = alloca + // CHECK-NEXT: [[I:%[a-z0-9]+]] = getelementptr inbounds {{.*}}* [[LVALUE]], i32 0, i32 0 + // CHECK-NEXT: store i32 17, i32* [[I]] + // CHECK-NEXT: [[X:%[a-z0-9]+]] = getelementptr inbounds {{.*}} [[LVALUE]], i32 0, i32 1 + // CHECK-NEXT: call void @_ZN1XC1EPKc({{.*}}[[X]] + // CHECK-NEXT: [[I:%[a-z0-9]+]] = getelementptr inbounds {{.*}} [[LVALUE]], i32 0, i32 0 + // CHECK-NEXT: [[RESULT:%[a-z0-9]+]] = load i32* + // CHECK-NEXT: call void @_ZN1YD1Ev + // CHECK-NEXT: ret i32 [[RESULT]] + return ((Y){17, "seventeen"}).i; +} diff --git a/test/CodeGenCXX/const-init.cpp b/test/CodeGenCXX/const-init.cpp index a8c6f304980b..797d1377f698 100644 --- a/test/CodeGenCXX/const-init.cpp +++ b/test/CodeGenCXX/const-init.cpp @@ -10,7 +10,7 @@ void f(); void (&fr)() = f; struct S { int& a; }; -// CHECK: @s = global %0 { i32* @a } +// CHECK: @s = global %struct.S { i32* @a } S s = { a }; // PR5581 @@ -21,7 +21,7 @@ public: unsigned f; }; -// CHECK: @_ZN6PR55812g0E = global %1 { i32 1 } +// CHECK: @_ZN6PR55812g0E = global %"class.PR5581::C" { i32 1 } C g0 = { C::e1 }; } diff --git a/test/CodeGenCXX/constructor-init.cpp b/test/CodeGenCXX/constructor-init.cpp index 47e3b7b0bb90..a195afe0692f 100644 --- a/test/CodeGenCXX/constructor-init.cpp +++ b/test/CodeGenCXX/constructor-init.cpp @@ -113,6 +113,22 @@ namespace InitVTable { B::B(int x) : A(x + 5) {} } +namespace rdar9694300 { + struct X { + int x; + }; + + // CHECK: define void @_ZN11rdar96943001fEv + void f() { + // CHECK: alloca + X x; + // CHECK-NEXT: [[I:%.*]] = alloca i32 + // CHECK-NEXT: store i32 17, i32* [[I]] + int i = 17; + // CHECK-NEXT: ret void + } +} + template<typename T> struct X { X(const X &); diff --git a/test/CodeGenCXX/constructors.cpp b/test/CodeGenCXX/constructors.cpp index 75588ce06ce0..ec7f06c868a9 100644 --- a/test/CodeGenCXX/constructors.cpp +++ b/test/CodeGenCXX/constructors.cpp @@ -83,12 +83,12 @@ struct D : A { D::D(int x, ...) : A(ValueClass(x, x+1)), mem(x*x) {} -// CHECK: define void @_ZN1DC1Eiz(%struct.B* %this, i32 %x, ...) unnamed_addr +// CHECK: define void @_ZN1DC1Eiz(%struct.D* %this, i32 %x, ...) unnamed_addr // CHECK: call void @_ZN10ValueClassC1Eii( // CHECK: call void @_ZN1AC2E10ValueClass( // CHECK: call void @_ZN6MemberC1Ei( -// CHECK: define void @_ZN1DC2Eiz(%struct.B* %this, i32 %x, ...) unnamed_addr +// CHECK: define void @_ZN1DC2Eiz(%struct.D* %this, i32 %x, ...) unnamed_addr // CHECK: call void @_ZN10ValueClassC1Eii( // CHECK: call void @_ZN1AC2E10ValueClass( // CHECK: call void @_ZN6MemberC1Ei( @@ -104,3 +104,14 @@ namespace test0 { C tmp = in; } } + +namespace test1 { + struct A { A(); void *ptr; }; + struct B { B(); int x; A a[0]; }; + B::B() {} + // CHECK: define void @_ZN5test11BC2Ev( + // CHECK: [[THIS:%.*]] = load [[B:%.*]]** + // CHECK-NEXT: [[A:%.*]] = getelementptr inbounds [[B:%.*]]* [[THIS]], i32 0, i32 1 + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [0 x {{%.*}}]* [[A]], i32 0, i32 0 + // CHECK-NEXT: ret void +} diff --git a/test/CodeGenCXX/copy-constructor-elim-2.cpp b/test/CodeGenCXX/copy-constructor-elim-2.cpp index 4f4a8e998bee..a4a688f737d7 100644 --- a/test/CodeGenCXX/copy-constructor-elim-2.cpp +++ b/test/CodeGenCXX/copy-constructor-elim-2.cpp @@ -3,7 +3,7 @@ struct A { int x; A(int); ~A(); }; A f() { return A(0); } // CHECK: define void @_Z1fv -// CHECK: call void @_ZN1AC1Ei +// CHECK: call {{.*}} @_ZN1AC1Ei // CHECK-NEXT: ret void // Verify that we do not elide copies when constructing a base class. @@ -21,14 +21,14 @@ namespace no_elide_base { Derived(const Other &O); }; - // CHECK: define void @_ZN13no_elide_base7DerivedC1ERKNS_5OtherE(%"struct.no_elide_base::Derived"* %this, %"struct.PR8683::A"* %O) unnamed_addr + // CHECK: define {{.*}} @_ZN13no_elide_base7DerivedC1ERKNS_5OtherE(%"struct.no_elide_base::Derived"* %this, %"struct.no_elide_base::Other"* %O) unnamed_addr Derived::Derived(const Other &O) - // CHECK: call void @_ZNK13no_elide_base5OthercvNS_4BaseEEv - // CHECK: call void @_ZN13no_elide_base4BaseC2ERKS0_ - // CHECK: call void @_ZN13no_elide_base4BaseD1Ev + // CHECK: call {{.*}} @_ZNK13no_elide_base5OthercvNS_4BaseEEv + // CHECK: call {{.*}} @_ZN13no_elide_base4BaseC2ERKS0_ + // CHECK: call {{.*}} @_ZN13no_elide_base4BaseD1Ev : Base(O) { - // CHECK: ret void + // CHECK: ret } } @@ -48,7 +48,7 @@ struct B { void f() { // Verify that we don't mark the copy constructor in this expression as elidable. - // CHECK: call void @_ZN6PR86831AC1ERKS0_ + // CHECK: call {{.*}} @_ZN6PR86831AC1ERKS0_ A a = (B().a); } diff --git a/test/CodeGenCXX/copy-constructor-synthesis-2.cpp b/test/CodeGenCXX/copy-constructor-synthesis-2.cpp index a5566797d48b..d028a28fdd67 100644 --- a/test/CodeGenCXX/copy-constructor-synthesis-2.cpp +++ b/test/CodeGenCXX/copy-constructor-synthesis-2.cpp @@ -3,5 +3,5 @@ struct A { virtual void a(); }; A x(A& y) { return y; } -// CHECK: define linkonce_odr void @_ZN1AC1ERKS_(%struct.A* %this, %struct.A*) unnamed_addr +// CHECK: define linkonce_odr {{.*}} @_ZN1AC1ERKS_(%struct.A* %this, %struct.A*) unnamed_addr // CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1A, i64 0, i64 2) diff --git a/test/CodeGenCXX/copy-initialization.cpp b/test/CodeGenCXX/copy-initialization.cpp index 62b9f2678a43..aecd64ec1aac 100644 --- a/test/CodeGenCXX/copy-initialization.cpp +++ b/test/CodeGenCXX/copy-initialization.cpp @@ -12,7 +12,7 @@ struct Bar { void f(Foo); -// CHECK: define void @_Z1g3Foo(%struct.Bar* %foo) +// CHECK: define void @_Z1g3Foo(%struct.Foo* %foo) void g(Foo foo) { // CHECK: call void @_ZN3BarC1Ev // CHECK: @_ZNK3BarcvRK3FooEv diff --git a/test/CodeGenCXX/cxx0x-defaulted-templates.cpp b/test/CodeGenCXX/cxx0x-defaulted-templates.cpp index 3d4000e4994c..09eb4fe7a7f6 100644 --- a/test/CodeGenCXX/cxx0x-defaulted-templates.cpp +++ b/test/CodeGenCXX/cxx0x-defaulted-templates.cpp @@ -5,15 +5,15 @@ struct X { X(); }; -// CHECK: define void @_ZN1XIbEC1Ev -// CHECK: define void @_ZN1XIbEC2Ev +// CHECK: define {{.*}} @_ZN1XIbEC1Ev +// CHECK: define {{.*}} @_ZN1XIbEC2Ev template <> X<bool>::X() = default; -// CHECK: define weak_odr void @_ZN1XIiEC1Ev -// CHECK: define weak_odr void @_ZN1XIiEC2Ev +// CHECK: define weak_odr {{.*}} @_ZN1XIiEC1Ev +// CHECK: define weak_odr {{.*}} @_ZN1XIiEC2Ev template <typename T> X<T>::X() = default; template X<int>::X(); -// CHECK: define linkonce_odr void @_ZN1XIcEC1Ev -// CHECK: define linkonce_odr void @_ZN1XIcEC2Ev +// CHECK: define linkonce_odr {{.*}} @_ZN1XIcEC1Ev +// CHECK: define linkonce_odr {{.*}} @_ZN1XIcEC2Ev X<char> x; diff --git a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp index 15c8e7f5d827..0bac492796f8 100644 --- a/test/CodeGenCXX/cxx0x-delegating-ctors.cpp +++ b/test/CodeGenCXX/cxx0x-delegating-ctors.cpp @@ -26,28 +26,28 @@ delegator::delegator() { delegator::delegator(bool) {} -// CHECK: define void @_ZN9delegatorC1Ec -// CHECK: void @_ZN9delegatorC1Eb +// CHECK: define {{.*}} @_ZN9delegatorC1Ec +// CHECK: {{.*}} @_ZN9delegatorC1Eb // CHECK: void @__cxa_throw // CHECK: void @_ZSt9terminatev -// CHECK: void @_ZN9delegatorD1Ev -// CHECK: define void @_ZN9delegatorC2Ec -// CHECK: void @_ZN9delegatorC2Eb +// CHECK: {{.*}} @_ZN9delegatorD1Ev +// CHECK: define {{.*}} @_ZN9delegatorC2Ec +// CHECK: {{.*}} @_ZN9delegatorC2Eb // CHECK: void @__cxa_throw // CHECK: void @_ZSt9terminatev -// CHECK: void @_ZN9delegatorD2Ev +// CHECK: {{.*}} @_ZN9delegatorD2Ev delegator::delegator(char) : delegator(true) { throw 0; } -// CHECK: define void @_ZN9delegatorC1Ei -// CHECK: void @_ZN9delegatorC1Ev +// CHECK: define {{.*}} @_ZN9delegatorC1Ei +// CHECK: {{.*}} @_ZN9delegatorC1Ev // CHECK-NOT: void @_ZSt9terminatev // CHECK: ret // CHECK-NOT: void @_ZSt9terminatev -// CHECK: define void @_ZN9delegatorC2Ei -// CHECK: void @_ZN9delegatorC2Ev +// CHECK: define {{.*}} @_ZN9delegatorC2Ei +// CHECK: {{.*}} @_ZN9delegatorC2Ev // CHECK-NOT: void @_ZSt9terminatev // CHECK: ret // CHECK-NOT: void @_ZSt9terminatev diff --git a/test/CodeGenCXX/default-constructor-default-argument.cpp b/test/CodeGenCXX/default-constructor-default-argument.cpp index f2c7f6d12a34..374a967e3e2a 100644 --- a/test/CodeGenCXX/default-constructor-default-argument.cpp +++ b/test/CodeGenCXX/default-constructor-default-argument.cpp @@ -5,4 +5,4 @@ struct A { A(int x = 2); }; struct B : public A {}; B x; -// CHECK: call void @_ZN1AC2Ei +// CHECK: call {{.*}} @_ZN1AC2Ei diff --git a/test/CodeGenCXX/default-constructor-template-member.cpp b/test/CodeGenCXX/default-constructor-template-member.cpp index 422cc099ad6c..0dd64dfcb4c9 100644 --- a/test/CodeGenCXX/default-constructor-template-member.cpp +++ b/test/CodeGenCXX/default-constructor-template-member.cpp @@ -5,6 +5,6 @@ struct B { A<int> x; }; void a() { B b; } -// CHECK: call void @_ZN1BC1Ev -// CHECK: define linkonce_odr void @_ZN1BC1Ev(%struct.B* %this) unnamed_addr -// CHECK: call void @_ZN1AIiEC1Ev +// CHECK: call {{.*}} @_ZN1BC1Ev +// CHECK: define linkonce_odr {{.*}} @_ZN1BC1Ev(%struct.B* %this) unnamed_addr +// CHECK: call {{.*}} @_ZN1AIiEC1Ev diff --git a/test/CodeGenCXX/delete.cpp b/test/CodeGenCXX/delete.cpp index ddc7bb8dac28..08ce0de3b5c3 100644 --- a/test/CodeGenCXX/delete.cpp +++ b/test/CodeGenCXX/delete.cpp @@ -54,7 +54,7 @@ namespace test0 { delete a; } - // CHECK: define linkonce_odr void @_ZN5test01AD1Ev(%class.A* %this) unnamed_addr + // CHECK: define linkonce_odr void @_ZN5test01AD1Ev(%"struct.test0::A"* %this) unnamed_addr // CHECK: define linkonce_odr void @_ZN5test01AdlEPv } @@ -67,31 +67,22 @@ namespace test1 { // CHECK: define void @_ZN5test14testEPA10_A20_NS_1AE( void test(A (*arr)[10][20]) { delete [] arr; - // CHECK: icmp eq [10 x [20 x [[S:%.*]]]]* [[PTR:%.*]], null + // CHECK: icmp eq [10 x [20 x [[A:%.*]]]]* [[PTR:%.*]], null // CHECK-NEXT: br i1 - // CHECK: [[ARR:%.*]] = getelementptr inbounds [10 x [20 x [[S]]]]* [[PTR]], i32 0, i32 0, i32 0 - // CHECK-NEXT: bitcast {{.*}} to i8* - // CHECK-NEXT: [[ALLOC:%.*]] = getelementptr inbounds {{.*}}, i64 -8 - // CHECK-NEXT: bitcast i8* [[ALLOC]] to i64* - // CHECK-NEXT: load - // CHECK-NEXT: store i64 {{.*}}, i64* [[IDX:%.*]] - - // CHECK: load i64* [[IDX]] - // CHECK-NEXT: icmp ne {{.*}}, 0 - // CHECK-NEXT: br i1 - - // CHECK: load i64* [[IDX]] - // CHECK-NEXT: [[I:%.*]] = sub i64 {{.*}}, 1 - // CHECK-NEXT: getelementptr inbounds [[S]]* [[ARR]], i64 [[I]] - // CHECK-NEXT: call void @_ZN5test11AD1Ev( - // CHECK-NEXT: br label - - // CHECK: load i64* [[IDX]] - // CHECK-NEXT: sub - // CHECK-NEXT: store {{.*}}, i64* [[IDX]] - // CHECK-NEXT: br label - + // CHECK: [[BEGIN:%.*]] = getelementptr inbounds [10 x [20 x [[A]]]]* [[PTR]], i32 0, i32 0, i32 0 + // CHECK-NEXT: [[T0:%.*]] = bitcast [[A]]* [[BEGIN]] to i8* + // CHECK-NEXT: [[ALLOC:%.*]] = getelementptr inbounds i8* [[T0]], i64 -8 + // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[ALLOC]] to i64* + // CHECK-NEXT: [[COUNT:%.*]] = load i64* [[T1]] + // CHECK: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 [[COUNT]] + // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq [[A]]* [[BEGIN]], [[END]] + // CHECK-NEXT: br i1 [[ISEMPTY]], + // CHECK: [[PAST:%.*]] = phi [[A]]* [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]]* [[PAST]], i64 -1 + // CHECK-NEXT: call void @_ZN5test11AD1Ev([[A]]* [[CUR]]) + // CHECK-NEXT: [[ISDONE:%.*]] = icmp eq [[A]]* [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[ISDONE]] // CHECK: call void @_ZdaPv(i8* [[ALLOC]]) } } @@ -112,3 +103,22 @@ namespace test3 { delete a; } } + +namespace test4 { + // PR10341: ::delete with a virtual destructor + struct X { + virtual ~X(); + void operator delete (void *); + }; + + // CHECK: define void @_ZN5test421global_delete_virtualEPNS_1XE + void global_delete_virtual(X *xp) { + // CHECK: [[VTABLE:%.*]] = load void ([[X:%.*]])*** + // CHECK-NEXT: [[VFN:%.*]] = getelementptr inbounds void ([[X]])** [[VTABLE]], i64 0 + // CHECK-NEXT: [[VFNPTR:%.*]] = load void ([[X]])** [[VFN]] + // CHECK-NEXT: call void [[VFNPTR]]([[X]] [[OBJ:%.*]]) + // CHECK-NEXT: [[OBJVOID:%.*]] = bitcast [[X]] [[OBJ]] to i8* + // CHECK-NEXT: call void @_ZdlPv(i8* [[OBJVOID]]) nounwind + ::delete xp; + } +} diff --git a/test/CodeGenCXX/destructors.cpp b/test/CodeGenCXX/destructors.cpp index 94d88334f85b..33819858149b 100644 --- a/test/CodeGenCXX/destructors.cpp +++ b/test/CodeGenCXX/destructors.cpp @@ -40,11 +40,11 @@ namespace PR7526 { struct allocator_derived : allocator { }; - // CHECK: define void @_ZN6PR75269allocatorD2Ev(%"struct.PR5529::A"* %this) unnamed_addr + // CHECK: define void @_ZN6PR75269allocatorD2Ev(%"struct.PR7526::allocator"* %this) unnamed_addr // CHECK: call void @__cxa_call_unexpected allocator::~allocator() throw() { foo(); } - // CHECK: define linkonce_odr void @_ZN6PR752617allocator_derivedD1Ev(%"struct.PR5529::A"* %this) unnamed_addr + // CHECK: define linkonce_odr void @_ZN6PR752617allocator_derivedD1Ev(%"struct.PR7526::allocator_derived"* %this) unnamed_addr // CHECK-NOT: call void @__cxa_call_unexpected // CHECK: } void foo() { @@ -145,10 +145,10 @@ namespace test1 { P::~P() {} // CHECK: define void @_ZN5test11PD2Ev(%"struct.test1::P"* %this) unnamed_addr struct Q : A, B { ~Q(); }; - Q::~Q() {} // CHECK: define void @_ZN5test11QD2Ev(%"struct.test1::M"* %this) unnamed_addr + Q::~Q() {} // CHECK: define void @_ZN5test11QD2Ev(%"struct.test1::Q"* %this) unnamed_addr struct R : A { ~R(); }; - R::~R() { A a; } // CHECK: define void @_ZN5test11RD2Ev(%"struct.test1::M"* %this) unnamed_addr + R::~R() { A a; } // CHECK: define void @_ZN5test11RD2Ev(%"struct.test1::R"* %this) unnamed_addr struct S : A { ~S(); int x; }; S::~S() {} // alias tested above @@ -168,7 +168,7 @@ namespace test2 { struct B : A { ~B(); }; B::~B() {} - // CHECK: define void @_ZN5test21BD2Ev(%"struct.test1::M"* %this) unnamed_addr + // CHECK: define void @_ZN5test21BD2Ev(%"struct.test2::B"* %this) unnamed_addr // CHECK: call void @_ZN5test21AD2Ev } @@ -233,28 +233,28 @@ namespace test4 { namespace test5 { struct A { ~A(); }; - // This is really unnecessarily verbose; we should be using phis, - // even at -O0. - // CHECK: define void @_ZN5test53fooEv() // CHECK: [[ELEMS:%.*]] = alloca [5 x [[A:%.*]]], align - // CHECK-NEXT: [[IVAR:%.*]] = alloca i64 - // CHECK: [[ELEMSARRAY:%.*]] = bitcast [5 x [[A]]]* [[ELEMS]] to [[A]] - // CHECK-NEXT: store i64 5, i64* [[IVAR]] - // CHECK-NEXT: br label - // CHECK: [[I:%.*]] = load i64* [[IVAR]] - // CHECK-NEXT: icmp ne i64 [[I]], 0 - // CHECK-NEXT: br i1 - // CHECK: [[I:%.*]] = load i64* [[IVAR]] - // CHECK-NEXT: [[I2:%.*]] = sub i64 [[I]], 1 - // CHECK-NEXT: getelementptr inbounds [[A]]* [[ELEMSARRAY]], i64 [[I2]] - // CHECK-NEXT: call void @_ZN5test51AD1Ev( - // CHECK-NEXT: br label - // CHECK: [[I:%.*]] = load i64* [[IVAR]] - // CHECK-NEXT: [[I1:%.*]] = sub i64 [[I]], 1 - // CHECK-NEXT: store i64 [[I1]], i64* [[IVAR]] + // CHECK-NEXT: [[EXN:%.*]] = alloca i8* + // CHECK-NEXT: [[SEL:%.*]] = alloca i32 + // CHECK-NEXT: [[EHCLEANUP:%.*]] = alloca i32 + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [5 x [[A]]]* [[ELEMS]], i32 0, i32 0 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 5 // CHECK-NEXT: br label + // CHECK: [[POST:%.*]] = phi [[A]]* [ [[END]], {{%.*}} ], [ [[ELT:%.*]], {{%.*}} ] + // CHECK-NEXT: [[ELT]] = getelementptr inbounds [[A]]* [[POST]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test51AD1Ev([[A]]* [[ELT]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[ELT]], [[BEGIN]] + // CHECK-NEXT: br i1 [[T0]], // CHECK: ret void + // lpad + // CHECK: [[EMPTY:%.*]] = icmp eq [[A]]* [[BEGIN]], [[ELT]] + // CHECK-NEXT: br i1 [[EMPTY]] + // CHECK: [[AFTER:%.*]] = phi [[A]]* [ [[ELT]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test51AD1Ev([[A]]* [[CUR]]) + // CHECK: [[DONE:%.*]] = icmp eq [[A]]* [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[DONE]], void foo() { A elems[5]; } diff --git a/test/CodeGenCXX/eh.cpp b/test/CodeGenCXX/eh.cpp index 44219b4f1754..58cb44515ddf 100644 --- a/test/CodeGenCXX/eh.cpp +++ b/test/CodeGenCXX/eh.cpp @@ -14,7 +14,7 @@ void test1() { // CHECK-NEXT: [[EXN:%.*]] = bitcast i8* [[EXNOBJ]] to [[DSTAR:%[^*]*\*]] // CHECK-NEXT: [[EXN2:%.*]] = bitcast [[DSTAR]] [[EXN]] to i8* // CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64(i8* [[EXN2]], i8* bitcast ([[DSTAR]] @d1 to i8*), i64 8, i32 8, i1 false) -// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast (%0* @_ZTI7test1_D to i8*), i8* null) noreturn +// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({ i8*, i8* }* @_ZTI7test1_D to i8*), i8* null) noreturn // CHECK-NEXT: unreachable @@ -38,7 +38,7 @@ void test2() { // CHECK-NEXT: invoke void @_ZN7test2_DC1ERKS_([[DSTAR]] [[EXN]], [[DSTAR]] @d2) // CHECK-NEXT: to label %[[CONT:.*]] unwind label %{{.*}} // : [[CONT]]: (can't check this in Release-Asserts builds) -// CHECK: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast (%{{.*}}* @_ZTI7test2_D to i8*), i8* null) noreturn +// CHECK: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({{.*}}* @_ZTI7test2_D to i8*), i8* null) noreturn // CHECK-NEXT: unreachable @@ -56,7 +56,7 @@ void test3() { // CHECK: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 8) // CHECK-NEXT: [[EXN:%.*]] = bitcast i8* [[EXNOBJ]] to [[D:%[^*]+]]** // CHECK-NEXT: store [[D]]* null, [[D]]** [[EXN]] -// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast (%1* @_ZTIPV7test3_D to i8*), i8* null) noreturn +// CHECK-NEXT: call void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({ i8*, i8*, i32, i8* }* @_ZTIPV7test3_D to i8*), i8* null) noreturn // CHECK-NEXT: unreachable @@ -84,10 +84,10 @@ namespace test5 { // CHECK: [[EXNOBJ:%.*]] = call i8* @__cxa_allocate_exception(i64 1) // CHECK: [[EXNCAST:%.*]] = bitcast i8* [[EXNOBJ]] to [[A:%[^*]*]]* // CHECK-NEXT: invoke void @_ZN5test51AC1Ev([[A]]* [[EXNCAST]]) -// CHECK: invoke void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({{%.*}}* @_ZTIN5test51AE to i8*), i8* bitcast (void ([[A]]*)* @_ZN5test51AD1Ev to i8*)) noreturn +// CHECK: invoke void @__cxa_throw(i8* [[EXNOBJ]], i8* bitcast ({{.*}}* @_ZTIN5test51AE to i8*), i8* bitcast (void ([[A]]*)* @_ZN5test51AD1Ev to i8*)) noreturn // CHECK-NEXT: to label {{%.*}} unwind label %[[HANDLER:[^ ]*]] // : [[HANDLER]]: (can't check this in Release-Asserts builds) -// CHECK: {{%.*}} = call i32 @llvm.eh.typeid.for(i8* bitcast ({{%.*}}* @_ZTIN5test51AE to i8*)) +// CHECK: {{%.*}} = call i32 @llvm.eh.typeid.for(i8* bitcast ({{.*}}* @_ZTIN5test51AE to i8*)) } namespace test6 { @@ -177,11 +177,11 @@ namespace test9 { struct A { A(); }; - // CHECK: define void @_ZN5test91AC1Ev(%"struct.test10::A"* %this) unnamed_addr + // CHECK: define void @_ZN5test91AC1Ev(%"struct.test9::A"* %this) unnamed_addr // CHECK: call void @_ZN5test91AC2Ev // CHECK-NEXT: ret void - // CHECK: define void @_ZN5test91AC2Ev(%"struct.test10::A"* %this) unnamed_addr + // CHECK: define void @_ZN5test91AC2Ev(%"struct.test9::A"* %this) unnamed_addr A::A() try { // CHECK: invoke void @_ZN5test96opaqueEv() opaque(); diff --git a/test/CodeGenCXX/elide-call-reference.cpp b/test/CodeGenCXX/elide-call-reference.cpp index c82eee70c840..55d30e24c102 100644 --- a/test/CodeGenCXX/elide-call-reference.cpp +++ b/test/CodeGenCXX/elide-call-reference.cpp @@ -7,5 +7,5 @@ void b() { A x = a(); } -// CHECK: call void @_ZN1AC1ERKS_ -// CHECK: call void @_ZN1AD1Ev +// CHECK: call {{.*}} @_ZN1AC1ERKS_ +// CHECK: call {{.*}} @_ZN1AD1Ev diff --git a/test/CodeGenCXX/for-range.cpp b/test/CodeGenCXX/for-range.cpp index af4664462351..ab1a2317ba93 100644 --- a/test/CodeGenCXX/for-range.cpp +++ b/test/CodeGenCXX/for-range.cpp @@ -69,8 +69,8 @@ void for_range() { A a; for (B b : C()) { // CHECK: call void @_ZN1CC1Ev( - // CHECK: = call %struct.A* @_ZSt5beginR1C( - // CHECK: = call %struct.A* @_ZSt3endR1C( + // CHECK: = call %struct.B* @_ZSt5beginR1C( + // CHECK: = call %struct.B* @_ZSt3endR1C( // CHECK: br label %[[COND:.*]] // CHECK: [[COND]]: @@ -101,8 +101,8 @@ void for_member_range() { A a; for (B b : D()) { // CHECK: call void @_ZN1DC1Ev( - // CHECK: = call %struct.A* @_ZN1D5beginEv( - // CHECK: = call %struct.A* @_ZN1D3endEv( + // CHECK: = call %struct.B* @_ZN1D5beginEv( + // CHECK: = call %struct.B* @_ZN1D3endEv( // CHECK: br label %[[COND:.*]] // CHECK: [[COND]]: diff --git a/test/CodeGenCXX/global-init.cpp b/test/CodeGenCXX/global-init.cpp index 9bd7390d076c..053210bdfbec 100644 --- a/test/CodeGenCXX/global-init.cpp +++ b/test/CodeGenCXX/global-init.cpp @@ -21,21 +21,21 @@ struct D { ~D(); }; // PR6205: The casts should not require global initializers // CHECK: @_ZN6PR59741cE = external global %"struct.PR5974::C" // CHECK: @_ZN6PR59741aE = global %"struct.PR5974::A"* getelementptr inbounds (%"struct.PR5974::C"* @_ZN6PR59741cE, i32 0, i32 0) -// CHECK: @_ZN6PR59741bE = global %"struct.PR5974::A"* bitcast (i8* getelementptr (i8* bitcast (%"struct.PR5974::C"* @_ZN6PR59741cE to i8*), i64 4) to %"struct.PR5974::A"*), align 8 +// CHECK: @_ZN6PR59741bE = global %"struct.PR5974::B"* bitcast (i8* getelementptr (i8* bitcast (%"struct.PR5974::C"* @_ZN6PR59741cE to i8*), i64 4) to %"struct.PR5974::B"*), align 8 // CHECK: call void @_ZN1AC1Ev(%struct.A* @a) // CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) A a; -// CHECK: call void @_ZN1BC1Ev(%struct.A* @b) -// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1BD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @b, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) +// CHECK: call void @_ZN1BC1Ev(%struct.B* @b) +// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.B*)* @_ZN1BD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.B* @b, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) B b; // PR6205: this should not require a global initializer // CHECK-NOT: call void @_ZN1CC1Ev(%struct.C* @c) C c; -// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1DD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @d, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) +// CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.D*)* @_ZN1DD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.D* @d, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) D d; // <rdar://problem/7458115> @@ -70,6 +70,20 @@ namespace test3 { const char *test() { return var; } } +namespace test6 { + struct A { + A(); + }; + extern int foo(); + + // This needs an initialization function and guard variables. + // CHECK: load i8* bitcast (i64* @_ZGVN5test61xE + // CHECK: [[CALL:%.*]] = call i32 @_ZN5test63fooEv + // CHECK-NEXT: store i32 [[CALL]], i32* @_ZN5test61xE + // CHECK-NEXT: store i64 1, i64* @_ZGVN5test61xE + __attribute__((weak)) int x = foo(); +} + namespace PR5974 { struct A { int a; }; struct B { int b; }; @@ -97,15 +111,6 @@ namespace test5 { }; } -namespace test6 { - struct A { - A(); - }; - extern int foo(); - - // This needs an initialization function but not guard variables. - __attribute__((weak)) int x = foo(); -} // At the end of the file, we check that y is initialized before z. diff --git a/test/CodeGenCXX/implicit-copy-constructor.cpp b/test/CodeGenCXX/implicit-copy-constructor.cpp index 500860182ecc..8bc84a534b36 100644 --- a/test/CodeGenCXX/implicit-copy-constructor.cpp +++ b/test/CodeGenCXX/implicit-copy-constructor.cpp @@ -70,3 +70,13 @@ void test_X2() pimpl pdata; pdata.f0( new impl(*i)); } + +// rdar://problem/9598341 +namespace test3 { + struct A { A(const A&); A&operator=(const A&); }; + struct B { A a; unsigned : 0; }; + void test(const B &x) { + B y = x; + y = x; + } +} diff --git a/test/CodeGenCXX/init-incomplete-type.cpp b/test/CodeGenCXX/init-incomplete-type.cpp index 3312d3e04b71..1755dfb7beb1 100644 --- a/test/CodeGenCXX/init-incomplete-type.cpp +++ b/test/CodeGenCXX/init-incomplete-type.cpp @@ -10,3 +10,22 @@ static struct Bar<int> bar[1] = { { 0 } }; + + +namespace incomplete_type_refs { + struct A; + extern A g[]; + void foo(A*); + void f(void) { + foo(g); // Reference to array with unknown element type. + } + + struct A { // define the element type. + int a,b,c; + }; + + A *f2() { + return &g[1]; + } + +}
\ No newline at end of file diff --git a/test/CodeGenCXX/mangle-subst-std.cpp b/test/CodeGenCXX/mangle-subst-std.cpp index 837d4fa8570d..30b579ceb823 100644 --- a/test/CodeGenCXX/mangle-subst-std.cpp +++ b/test/CodeGenCXX/mangle-subst-std.cpp @@ -15,8 +15,8 @@ namespace std { struct A { A(); }; - // CHECK: define void @_ZNSt1AC1Ev(%"struct.N::std::A"* %this) unnamed_addr - // CHECK: define void @_ZNSt1AC2Ev(%"struct.N::std::A"* %this) unnamed_addr + // CHECK: define void @_ZNSt1AC1Ev(%"struct.std::A"* %this) unnamed_addr + // CHECK: define void @_ZNSt1AC2Ev(%"struct.std::A"* %this) unnamed_addr A::A() { } }; diff --git a/test/CodeGenCXX/mangle-template.cpp b/test/CodeGenCXX/mangle-template.cpp index 463f15dc4abe..f95e1521549d 100644 --- a/test/CodeGenCXX/mangle-template.cpp +++ b/test/CodeGenCXX/mangle-template.cpp @@ -82,7 +82,7 @@ namespace test7 { X(U*, typename int_c<(meta<T>::value + meta<U>::value)>::type *) { } }; - // CHECK: define weak_odr void @_ZN5test71XIiEC1IdEEPT_PNS_5int_cIXplL_ZNS_4metaIiE5valueEEsrNS6_IS3_EE5valueEE4typeE(%"class.test1::T"* %this, double*, float*) unnamed_addr + // CHECK: define weak_odr {{.*}} @_ZN5test71XIiEC1IdEEPT_PNS_5int_cIXplL_ZNS_4metaIiE5valueEEsr4metaIS3_EE5valueEE4typeE(%"struct.test7::X"* %this, double*, float*) unnamed_addr template X<int>::X(double*, float*); } @@ -101,7 +101,7 @@ namespace test8 { template<typename T> void f(int_c<meta<T>::type::value>) { } - // CHECK: define weak_odr void @_ZN5test81fIiEEvNS_5int_cIXsrNS_4metaIT_E4typeE5valueEEE + // CHECK: define weak_odr void @_ZN5test81fIiEEvNS_5int_cIXsr4metaIT_E4typeE5valueEEE template void f<int>(int_c<sizeof(int)>); } diff --git a/test/CodeGenCXX/mangle.cpp b/test/CodeGenCXX/mangle.cpp index 01dcf8b2305a..453b7b713ac6 100644 --- a/test/CodeGenCXX/mangle.cpp +++ b/test/CodeGenCXX/mangle.cpp @@ -196,9 +196,9 @@ template<typename T> struct __enable_if<true, T> { // PR5063 template<typename T> typename __enable_if<__is_scalar_type<T>::__value, void>::__type ft7() { } -// CHECK: @_Z3ft7IiEN11__enable_ifIXsr16__is_scalar_typeIT_E7__valueEvE6__typeEv +// CHECK: @_Z3ft7IiEN11__enable_ifIXsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv template void ft7<int>(); -// CHECK: @_Z3ft7IPvEN11__enable_ifIXsr16__is_scalar_typeIT_E7__valueEvE6__typeEv +// CHECK: @_Z3ft7IPvEN11__enable_ifIXsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv template void ft7<void*>(); // PR5144 @@ -226,9 +226,9 @@ S7::S7() {} // PR5063 template<typename T> typename __enable_if<(__is_scalar_type<T>::__value), void>::__type ft8() { } -// CHECK: @_Z3ft8IiEN11__enable_ifIXsr16__is_scalar_typeIT_E7__valueEvE6__typeEv +// CHECK: @_Z3ft8IiEN11__enable_ifIXsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv template void ft8<int>(); -// CHECK: @_Z3ft8IPvEN11__enable_ifIXsr16__is_scalar_typeIT_E7__valueEvE6__typeEv +// CHECK: @_Z3ft8IPvEN11__enable_ifIXsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv template void ft8<void*>(); // PR5796 @@ -241,7 +241,7 @@ template<bool, typename> struct __enable_if {}; template<typename T> struct __enable_if<true, T> { typedef T __type; }; template<typename T> -// CHECK: define linkonce_odr void @_ZN6PR57968__fill_aIiEENS_11__enable_ifIXntsrNS_16__is_scalar_typeIT_EE7__valueEvE6__typeEv +// CHECK: define linkonce_odr void @_ZN6PR57968__fill_aIiEENS_11__enable_ifIXntsr16__is_scalar_typeIT_EE7__valueEvE6__typeEv typename __enable_if<!__is_scalar_type<T>::__value, void>::__type __fill_a() { }; void f() { __fill_a<int>(); } @@ -711,3 +711,150 @@ namespace test27 { b<A>(f); } } + +// An injected class name type in a unresolved-name. +namespace test28 { + template <class T> struct A { + enum { bit }; + }; + + template <class T> void foo(decltype(A<T>::A::bit) x); + + void test() { + foo<char>(A<char>::bit); + // CHECK: call void @_ZN6test283fooIcEEvDtsr1AIT_E1AE3bitE( + } +} + +// An enclosing template type parameter in an unresolved-name. +namespace test29 { + template <class T> struct A { + template <class U> static void foo(decltype(T::fn(U())) x); + }; + struct B { static int fn(int); static long fn(long); }; + + void test() { + A<B>::foo<int>(0); + // CHECK: call void @_ZN6test291AINS_1BEE3fooIiEEvDTclsrS1_2fncvT__EEE( + } +} + +// An enclosing template template parameter in an unresolved-name. +namespace test30 { + template <template <class> class T> struct A { + template <class U> static void foo(decltype(T<U>::fn()) x); + }; + template <class T> struct B { static T fn(); }; + + void test() { + A<B>::foo<int>(0); + // CHECK: call void @_ZN6test301AINS_1BEE3fooIiEEvDTclsrS1_IT_EE2fnEE( + } +} + +namespace test31 { // instantiation-dependent mangling of decltype + int x; + template<class T> auto f1(T p)->decltype(x) { return 0; } + // The return type in the mangling of the template signature + // is encoded as "i". + template<class T> auto f2(T p)->decltype(p) { return 0; } + // The return type in the mangling of the template signature + // is encoded as "Dtfp_E". + void g(int); + template<class T> auto f3(T p)->decltype(g(p)) {} + + // CHECK: define weak_odr i32 @_ZN6test312f1IiEEiT_( + template int f1(int); + // CHECK: define weak_odr i32 @_ZN6test312f2IiEEDtfp_ET_ + template int f2(int); + // CHECK: define weak_odr void @_ZN6test312f3IiEEDTcl1gfp_EET_ + template void f3(int); +} + +// PR10205 +namespace test32 { + template<typename T, int=T::value> struct A { + typedef int type; + }; + struct B { enum { value = 4 }; }; + + template <class T> typename A<T>::type foo() { return 0; } + void test() { + foo<B>(); + // CHECK: call i32 @_ZN6test323fooINS_1BEEENS_1AIT_XsrS3_5valueEE4typeEv() + } +} + +namespace test33 { + template <class T> struct X { + enum { value = T::value }; + }; + + template<typename T, int=X<T>::value> struct A { + typedef int type; + }; + struct B { enum { value = 4 }; }; + + template <class T> typename A<T>::type foo() { return 0; } + + void test() { + foo<B>(); + // CHECK: call i32 @_ZN6test333fooINS_1BEEENS_1AIT_Xsr1XIS3_EE5valueEE4typeEv() + } +} + +namespace test34 { + // Mangling for instantiation-dependent decltype expressions. + template<typename T> + void f(decltype(sizeof(decltype(T() + T())))) {} + + // CHECK: define weak_odr void @_ZN6test341fIiEEvDTstDTplcvT__EcvS1__EEE + template void f<int>(decltype(sizeof(1))); + + // Mangling for non-instantiation-dependent sizeof expressions. + template<unsigned N> + void f2(int (&)[N + sizeof(int*)]) {} + + // CHECK: define weak_odr void @_ZN6test342f2ILj4EEEvRAplT_Lm8E_i + template void f2<4>(int (&)[4 + sizeof(int*)]); + + // Mangling for non-instantiation-dependent sizeof expressions + // involving an implicit conversion of the result of the sizeof. + template<unsigned long long N> + void f3(int (&)[N + sizeof(int*)]) {} + + // CHECK: define weak_odr void @_ZN6test342f3ILy4EEEvRAplT_Ly8E_i + template void f3<4>(int (&)[4 + sizeof(int*)]); + + // Mangling for instantiation-dependent sizeof() expressions as + // template arguments. + template<unsigned> struct A { }; + + template<typename T> void f4(::test34::A<sizeof(sizeof(decltype(T() + T())))>) { } + + // CHECK: define weak_odr void @_ZN6test342f4IiEEvNS_1AIXszstDTplcvT__EcvS2__EEEEE + template void f4<int>(A<sizeof(sizeof(int))>); +} + +namespace test35 { + // Dependent operator names of unknown arity. + struct A { + template<typename U> A operator+(U) const; + }; + + template<typename T> + void f1(decltype(sizeof(&T::template operator+<int>))) {} + + // CHECK: define weak_odr void @_ZN6test352f1INS_1AEEEvDTszadsrT_plIiEE + template void f1<A>(__SIZE_TYPE__); +} + +namespace test36 { + template<unsigned> struct A { }; + + template<typename ...Types> + auto f1(Types... values) -> A<sizeof...(values)> { } + + // CHECK: define weak_odr {{.*}} @_ZN6test362f1IJifEEENS_1AIXsZfp_EEEDpT_ + template A<2> f1(int, float); +} diff --git a/test/CodeGenCXX/member-function-pointers.cpp b/test/CodeGenCXX/member-function-pointers.cpp index 011e9cd68556..4c42bd8283ae 100644 --- a/test/CodeGenCXX/member-function-pointers.cpp +++ b/test/CodeGenCXX/member-function-pointers.cpp @@ -11,56 +11,56 @@ void (A::*volatile vpa)(); void (B::*pb)(); void (C::*pc)(); -// CHECK: @pa2 = global %0 { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 }, align 8 +// CHECK: @pa2 = global { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 }, align 8 void (A::*pa2)() = &A::f; -// CHECK: @pa3 = global %0 { i64 1, i64 0 }, align 8 -// CHECK-LP32: @pa3 = global %0 { i32 1, i32 0 }, align 4 +// CHECK: @pa3 = global { i64, i64 } { i64 1, i64 0 }, align 8 +// CHECK-LP32: @pa3 = global { i32, i32 } { i32 1, i32 0 }, align 4 void (A::*pa3)() = &A::vf1; -// CHECK: @pa4 = global %0 { i64 9, i64 0 }, align 8 -// CHECK-LP32: @pa4 = global %0 { i32 5, i32 0 }, align 4 +// CHECK: @pa4 = global { i64, i64 } { i64 9, i64 0 }, align 8 +// CHECK-LP32: @pa4 = global { i32, i32 } { i32 5, i32 0 }, align 4 void (A::*pa4)() = &A::vf2; -// CHECK: @pc2 = global %0 { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 16 }, align 8 +// CHECK: @pc2 = global { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 16 }, align 8 void (C::*pc2)() = &C::f; -// CHECK: @pc3 = global %0 { i64 1, i64 0 }, align 8 +// CHECK: @pc3 = global { i64, i64 } { i64 1, i64 0 }, align 8 void (A::*pc3)() = &A::vf1; void f() { - // CHECK: store %0 zeroinitializer, %0* @pa + // CHECK: store { i64, i64 } zeroinitializer, { i64, i64 }* @pa pa = 0; // Is this okay? What are LLVM's volatile semantics for structs? - // CHECK: volatile store %0 zeroinitializer, %0* @vpa + // CHECK: volatile store { i64, i64 } zeroinitializer, { i64, i64 }* @vpa vpa = 0; - // CHECK: [[TMP:%.*]] = load %0* @pa, align 8 - // CHECK: [[TMPADJ:%.*]] = extractvalue %0 [[TMP]], 1 + // CHECK: [[TMP:%.*]] = load { i64, i64 }* @pa, align 8 + // CHECK: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1 // CHECK: [[ADJ:%.*]] = add nsw i64 [[TMPADJ]], 16 - // CHECK: [[RES:%.*]] = insertvalue %0 [[TMP]], i64 [[ADJ]], 1 - // CHECK: store %0 [[RES]], %0* @pc, align 8 + // CHECK: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1 + // CHECK: store { i64, i64 } [[RES]], { i64, i64 }* @pc, align 8 pc = pa; - // CHECK: [[TMP:%.*]] = load %0* @pc, align 8 - // CHECK: [[TMPADJ:%.*]] = extractvalue %0 [[TMP]], 1 + // CHECK: [[TMP:%.*]] = load { i64, i64 }* @pc, align 8 + // CHECK: [[TMPADJ:%.*]] = extractvalue { i64, i64 } [[TMP]], 1 // CHECK: [[ADJ:%.*]] = sub nsw i64 [[TMPADJ]], 16 - // CHECK: [[RES:%.*]] = insertvalue %0 [[TMP]], i64 [[ADJ]], 1 - // CHECK: store %0 [[RES]], %0* @pa, align 8 + // CHECK: [[RES:%.*]] = insertvalue { i64, i64 } [[TMP]], i64 [[ADJ]], 1 + // CHECK: store { i64, i64 } [[RES]], { i64, i64 }* @pa, align 8 pa = static_cast<void (A::*)()>(pc); } void f2() { - // CHECK: store %0 { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 } + // CHECK: store { i64, i64 } { i64 ptrtoint (void (%struct.A*)* @_ZN1A1fEv to i64), i64 0 } void (A::*pa2)() = &A::f; - // CHECK: store %0 { i64 1, i64 0 } - // CHECK-LP32: store %0 { i32 1, i32 0 } + // CHECK: store { i64, i64 } { i64 1, i64 0 } + // CHECK-LP32: store { i32, i32 } { i32 1, i32 0 } void (A::*pa3)() = &A::vf1; - // CHECK: store %0 { i64 9, i64 0 } - // CHECK-LP32: store %0 { i32 5, i32 0 } + // CHECK: store { i64, i64 } { i64 9, i64 0 } + // CHECK-LP32: store { i32, i32 } { i32 5, i32 0 } void (A::*pa4)() = &A::vf2; } diff --git a/test/CodeGenCXX/member-init-assignment.cpp b/test/CodeGenCXX/member-init-assignment.cpp index 128cb888f3d4..84c4a36fe2db 100644 --- a/test/CodeGenCXX/member-init-assignment.cpp +++ b/test/CodeGenCXX/member-init-assignment.cpp @@ -10,8 +10,8 @@ struct Foo { Foo::Foo(unsigned arg) : file_id(arg = 42) { } -// CHECK: define void @_ZN3FooC2Ej(%struct.Foo* %this, i32 %arg) unnamed_addr +// CHECK: define {{.*}} @_ZN3FooC2Ej(%struct.Foo* %this, i32 %arg) unnamed_addr // CHECK: [[ARG:%.*]] = alloca i32 // CHECK: store i32 42, i32* [[ARG]] // CHECK: store i32 42, i32* %{{.*}} -// CHECK: ret void +// CHECK: ret {{void|%struct.Foo}} diff --git a/test/CodeGenCXX/member-init-ctor.cpp b/test/CodeGenCXX/member-init-ctor.cpp index d9a6734b319b..d70947bcab00 100644 --- a/test/CodeGenCXX/member-init-ctor.cpp +++ b/test/CodeGenCXX/member-init-ctor.cpp @@ -7,7 +7,7 @@ struct S { S s; -// CHECK: define{{.*}} void @_ZN1SC2Ev( +// CHECK: define {{.*}} @_ZN1SC2Ev( // CHECK-NOT } // CHECK: call {{.*}} @_Z1bv() // CHECK-NOT } diff --git a/test/CodeGenCXX/new-overflow.cpp b/test/CodeGenCXX/new-overflow.cpp new file mode 100644 index 000000000000..68f89c35b636 --- /dev/null +++ b/test/CodeGenCXX/new-overflow.cpp @@ -0,0 +1,209 @@ +// RUN: %clang_cc1 -triple i386-unknown-unknown %s -emit-llvm -o - | FileCheck %s + +// rdar://problem/9246208 + +// Basic test. +namespace test0 { + struct A { + A(); + int x; + }; + + typedef A elt; + + // CHECK: define [[A:%.*]]* @_ZN5test04testEs(i16 signext + // CHECK: [[N:%.*]] = sext i16 {{%.*}} to i32 + // CHECK-NEXT: [[T0:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[N]], i32 4) + // CHECK-NEXT: [[T1:%.*]] = extractvalue { i32, i1 } [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 + // CHECK-NEXT: [[T3:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T3]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] + elt *test(short s) { + return new elt[s]; + } +} + +// test0 with a nested array. +namespace test1 { + struct A { + A(); + int x; + }; + + typedef A elt[100]; + + // CHECK: define [100 x [[A:%.*]]]* @_ZN5test14testEs(i16 signext + // CHECK: [[N:%.*]] = sext i16 {{%.*}} to i32 + // CHECK-NEXT: [[T0:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[N]], i32 400) + // CHECK-NEXT: [[T1:%.*]] = extractvalue { i32, i1 } [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 + // CHECK-NEXT: [[T3:%.*]] = mul i32 [[N]], 100 + // CHECK-NEXT: [[T4:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T4]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[T3]] + elt *test(short s) { + return new elt[s]; + } +} + +// test1 with an array cookie. +namespace test2 { + struct A { + A(); + ~A(); + int x; + }; + + typedef A elt[100]; + + // CHECK: define [100 x [[A:%.*]]]* @_ZN5test24testEs(i16 signext + // CHECK: [[N:%.*]] = sext i16 {{%.*}} to i32 + // CHECK-NEXT: [[T0:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[N]], i32 400) + // CHECK-NEXT: [[T1:%.*]] = extractvalue { i32, i1 } [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 + // CHECK-NEXT: [[T3:%.*]] = mul i32 [[N]], 100 + // CHECK-NEXT: [[T4:%.*]] = call { i32, i1 } @llvm.uadd.with.overflow.i32(i32 [[T2]], i32 4) + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T4]], 1 + // CHECK-NEXT: [[T6:%.*]] = or i1 [[T1]], [[T5]] + // CHECK-NEXT: [[T7:%.*]] = extractvalue { i32, i1 } [[T4]], 0 + // CHECK-NEXT: [[T8:%.*]] = select i1 [[T6]], i32 -1, i32 [[T7]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T8]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[T3]] + elt *test(short s) { + return new elt[s]; + } +} + +// test0 with a 1-byte element. +namespace test4 { + struct A { + A(); + }; + + typedef A elt; + + // CHECK: define [[A:%.*]]* @_ZN5test44testEs(i16 signext + // CHECK: [[N:%.*]] = sext i16 {{%.*}} to i32 + // CHECK-NEXT: [[T0:%.*]] = icmp slt i32 [[N]], 0 + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i32 -1, i32 [[N]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T1]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] + elt *test(short s) { + return new elt[s]; + } +} + +// test4 with no sext required. +namespace test5 { + struct A { + A(); + }; + + typedef A elt; + + // CHECK: define [[A:%.*]]* @_ZN5test54testEi(i32 + // CHECK: [[N:%.*]] = load i32* + // CHECK-NEXT: [[T0:%.*]] = icmp slt i32 [[N]], 0 + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i32 -1, i32 [[N]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T1]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] + elt *test(int s) { + return new elt[s]; + } +} + +// test0 with an unsigned size. +namespace test6 { + struct A { + A(); + int x; + }; + + typedef A elt; + + // CHECK: define [[A:%.*]]* @_ZN5test64testEt(i16 zeroext + // CHECK: [[N:%.*]] = zext i16 {{%.*}} to i32 + // CHECK-NEXT: [[T0:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[N]], i32 4) + // CHECK-NEXT: [[T1:%.*]] = extractvalue { i32, i1 } [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 + // CHECK-NEXT: [[T3:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T3]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[N]] + elt *test(unsigned short s) { + return new elt[s]; + } +} + +// test1 with an unsigned size. +namespace test7 { + struct A { + A(); + int x; + }; + + typedef A elt[100]; + + // CHECK: define [100 x [[A:%.*]]]* @_ZN5test74testEt(i16 zeroext + // CHECK: [[N:%.*]] = zext i16 {{%.*}} to i32 + // CHECK-NEXT: [[T0:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[N]], i32 400) + // CHECK-NEXT: [[T1:%.*]] = extractvalue { i32, i1 } [[T0]], 1 + // CHECK-NEXT: [[T2:%.*]] = extractvalue { i32, i1 } [[T0]], 0 + // CHECK-NEXT: [[T3:%.*]] = mul i32 [[N]], 100 + // CHECK-NEXT: [[T4:%.*]] = select i1 [[T1]], i32 -1, i32 [[T2]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T4]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[T3]] + elt *test(unsigned short s) { + return new elt[s]; + } +} + +// test0 with a signed type larger than size_t. +namespace test8 { + struct A { + A(); + int x; + }; + + typedef A elt; + + // CHECK: define [[A:%.*]]* @_ZN5test84testEx(i64 + // CHECK: [[N:%.*]] = load i64* + // CHECK-NEXT: [[T0:%.*]] = icmp uge i64 [[N]], 4294967296 + // CHECK-NEXT: [[T1:%.*]] = trunc i64 [[N]] to i32 + // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[T1]], i32 4) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 1 + // CHECK-NEXT: [[T4:%.*]] = or i1 [[T0]], [[T3]] + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T2]], 0 + // CHECK-NEXT: [[T6:%.*]] = select i1 [[T4]], i32 -1, i32 [[T5]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T6]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[T1]] + elt *test(long long s) { + return new elt[s]; + } +} + +// test8 with an unsigned type. +namespace test9 { + struct A { + A(); + int x; + }; + + typedef A elt; + + // CHECK: define [[A:%.*]]* @_ZN5test94testEy(i64 + // CHECK: [[N:%.*]] = load i64* + // CHECK-NEXT: [[T0:%.*]] = icmp uge i64 [[N]], 4294967296 + // CHECK-NEXT: [[T1:%.*]] = trunc i64 [[N]] to i32 + // CHECK-NEXT: [[T2:%.*]] = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 [[T1]], i32 4) + // CHECK-NEXT: [[T3:%.*]] = extractvalue { i32, i1 } [[T2]], 1 + // CHECK-NEXT: [[T4:%.*]] = or i1 [[T0]], [[T3]] + // CHECK-NEXT: [[T5:%.*]] = extractvalue { i32, i1 } [[T2]], 0 + // CHECK-NEXT: [[T6:%.*]] = select i1 [[T4]], i32 -1, i32 [[T5]] + // CHECK-NEXT: call noalias i8* @_Znaj(i32 [[T6]]) + // CHECK: getelementptr inbounds {{.*}}, i32 [[T1]] + elt *test(unsigned long long s) { + return new elt[s]; + } +} diff --git a/test/CodeGenCXX/new.cpp b/test/CodeGenCXX/new.cpp index d14a5e251ce0..3a72bb84e7f6 100644 --- a/test/CodeGenCXX/new.cpp +++ b/test/CodeGenCXX/new.cpp @@ -182,12 +182,17 @@ namespace test15 { } // CHECK: define void @_ZN6test155test1EPv( - // CHECK: [[P:%.*]] = load i8* + // CHECK: [[P:%.*]] = load i8** // CHECK-NEXT: icmp eq i8* [[P]], null // CHECK-NEXT: br i1 - // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* - // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]]* [[T0]], - // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T1]]) + // CHECK: [[BEGIN:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 5 + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[CUR]]) + // CHECK-NEXT: [[NEXT]] = getelementptr inbounds [[A]]* [[CUR]], i64 1 + // CHECK-NEXT: [[DONE:%.*]] = icmp eq [[A]]* [[NEXT]], [[END]] + // CHECK-NEXT: br i1 [[DONE]] void test1(void *p) { new (p) A[5]; } @@ -202,10 +207,27 @@ namespace test15 { // CHECK-NEXT: [[P:%.*]] = load i8* // CHECK-NEXT: icmp eq i8* [[P]], null // CHECK-NEXT: br i1 - // CHECK: [[T0:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* - // CHECK: [[T1:%.*]] = getelementptr inbounds [[A]]* [[T0]], - // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[T1]]) + // CHECK: [[BEGIN:%.*]] = bitcast i8* [[P]] to [[A:%.*]]* + // CHECK-NEXT: [[ISEMPTY:%.*]] = icmp eq i64 [[T0]], 0 + // CHECK-NEXT: br i1 [[ISEMPTY]], + // CHECK: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 [[T0]] + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], + // CHECK-NEXT: call void @_ZN6test151AC1Ev([[A]]* [[CUR]]) void test2(void *p, int n) { new (p) A[n]; } } + +namespace PR10197 { + // CHECK: define weak_odr void @_ZN7PR101971fIiEEvv() + template<typename T> + void f() { + // CHECK: [[CALL:%.*]] = call noalias i8* @_Znwm + // CHECK-NEXT: [[CASTED:%.*]] = bitcast i8* [[CALL]] to + new T; + // CHECK-NEXT: ret void + } + + template void f<int>(); +} diff --git a/test/CodeGenCXX/noinline-template.cpp b/test/CodeGenCXX/noinline-template.cpp new file mode 100644 index 000000000000..6ee3935bc041 --- /dev/null +++ b/test/CodeGenCXX/noinline-template.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s + +// This was a problem in Sema, but only shows up as noinline missing +// in CodeGen. + +// CHECK: define linkonce_odr void @_ZN6VectorIiE13growStorageByEv(%struct.Vector* %this) nounwind noinline + +template <class Ty> struct Vector { + void growStorageBy(); +}; +template <class T> __attribute__((noinline)) void Vector<T>::growStorageBy() { +} +void foo() { + Vector<int> strs; + strs.growStorageBy(); +} diff --git a/test/CodeGenCXX/nrvo.cpp b/test/CodeGenCXX/nrvo.cpp index 82caff880f5c..0efb35538038 100644 --- a/test/CodeGenCXX/nrvo.cpp +++ b/test/CodeGenCXX/nrvo.cpp @@ -13,10 +13,10 @@ public: // CHECK-EH: define void @_Z5test0v X test0() { X x; - // CHECK: call void @_ZN1XC1Ev + // CHECK: call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: ret void - // CHECK-EH: call void @_ZN1XC1Ev + // CHECK-EH: call {{.*}} @_ZN1XC1Ev // CHECK-EH-NEXT: ret void return x; } @@ -24,13 +24,13 @@ X test0() { // CHECK: define void @_Z5test1b( // CHECK-EH: define void @_Z5test1b( X test1(bool B) { - // CHECK: tail call void @_ZN1XC1Ev + // CHECK: tail call {{.*}} @_ZN1XC1Ev // CHECK-NEXT: ret void X x; if (B) return (x); return x; - // CHECK-EH: tail call void @_ZN1XC1Ev + // CHECK-EH: tail call {{.*}} @_ZN1XC1Ev // CHECK-EH-NEXT: ret void } @@ -45,18 +45,18 @@ X test2(bool B) { return y; return x; - // CHECK: call void @_ZN1XC1Ev - // CHECK-NEXT: call void @_ZN1XC1Ev - // CHECK: call void @_ZN1XC1ERKS_ - // CHECK: call void @_ZN1XC1ERKS_ - // CHECK: call void @_ZN1XD1Ev - // CHECK: call void @_ZN1XD1Ev + // CHECK: call {{.*}} @_ZN1XC1Ev + // CHECK-NEXT: call {{.*}} @_ZN1XC1Ev + // CHECK: call {{.*}} @_ZN1XC1ERKS_ + // CHECK: call {{.*}} @_ZN1XC1ERKS_ + // CHECK: call {{.*}} @_ZN1XD1Ev + // CHECK: call {{.*}} @_ZN1XD1Ev // CHECK: ret void // The block ordering in the -fexceptions IR is unfortunate. - // CHECK-EH: call void @_ZN1XC1Ev - // CHECK-EH-NEXT: invoke void @_ZN1XC1Ev + // CHECK-EH: call {{.*}} @_ZN1XC1Ev + // CHECK-EH-NEXT: invoke {{.*}} @_ZN1XC1Ev // -> %invoke.cont, %lpad // %invoke.cont: @@ -64,7 +64,7 @@ X test2(bool B) { // -> %if.then, %if.end // %if.then: returning 'x' - // CHECK-EH: invoke void @_ZN1XC1ERKS_ + // CHECK-EH: invoke {{.*}} @_ZN1XC1ERKS_ // -> %cleanup, %lpad1 // %lpad: landing pad for ctor of 'y', dtor of 'y' @@ -74,23 +74,23 @@ X test2(bool B) { // -> %eh.cleanup // %lpad1: landing pad for return copy ctors, EH cleanup for 'y' - // CHECK-EH: invoke void @_ZN1XD1Ev + // CHECK-EH: invoke {{.*}} @_ZN1XD1Ev // -> %eh.cleanup, %terminate.lpad // %if.end: returning 'y' - // CHECK-EH: invoke void @_ZN1XC1ERKS_ + // CHECK-EH: invoke {{.*}} @_ZN1XC1ERKS_ // -> %cleanup, %lpad1 // %cleanup: normal cleanup for 'y' - // CHECK-EH: invoke void @_ZN1XD1Ev + // CHECK-EH: invoke {{.*}} @_ZN1XD1Ev // -> %invoke.cont11, %lpad // %invoke.cont11: normal cleanup for 'x' - // CHECK-EH: call void @_ZN1XD1Ev + // CHECK-EH: call {{.*}} @_ZN1XD1Ev // CHECK-EH-NEXT: ret void // %eh.cleanup: EH cleanup for 'x' - // CHECK-EH: invoke void @_ZN1XD1Ev + // CHECK-EH: invoke {{.*}} @_ZN1XD1Ev // -> %invoke.cont17, %terminate.lpad // %invoke.cont17: rethrow block for %eh.cleanup. @@ -121,13 +121,13 @@ extern "C" void exit(int) throw(); // CHECK: define void @_Z5test4b X test4(bool B) { { - // CHECK: tail call void @_ZN1XC1Ev + // CHECK: tail call {{.*}} @_ZN1XC1Ev X x; // CHECK: br i1 if (B) return x; } - // CHECK: tail call void @_ZN1XD1Ev + // CHECK: tail call {{.*}} @_ZN1XD1Ev // CHECK: tail call void @exit(i32 1) exit(1); } @@ -139,7 +139,7 @@ X test5() { try { may_throw(); } catch (X x) { - // CHECK-EH: invoke void @_ZN1XC1ERKS_ + // CHECK-EH: invoke {{.*}} @_ZN1XC1ERKS_ // CHECK-EH: call void @__cxa_end_catch() // CHECK-EH: ret void return x; diff --git a/test/CodeGenCXX/partial-destruction.cpp b/test/CodeGenCXX/partial-destruction.cpp new file mode 100644 index 000000000000..82deca06cf80 --- /dev/null +++ b/test/CodeGenCXX/partial-destruction.cpp @@ -0,0 +1,169 @@ +// RUN: %clang_cc1 %s -triple=x86_64-apple-darwin10 -emit-llvm -o - -fcxx-exceptions -fexceptions | FileCheck %s + +// Test IR generation for partial destruction of aggregates. + +void opaque(); + +// Initializer lists. +namespace test0 { + struct A { A(int); A(); ~A(); void *v; }; + void test() { + A as[10] = { 5, 7 }; + opaque(); + } + // CHECK: define void @_ZN5test04testEv() + // CHECK: [[AS:%.*]] = alloca [10 x [[A:%.*]]], align + // CHECK-NEXT: [[ENDVAR:%.*]] = alloca [[A]]* + // CHECK-NEXT: [[EXN:%.*]] = alloca i8* + // CHECK-NEXT: [[SEL:%.*]] = alloca i32 + // CHECK-NEXT: [[CLEANUP:%.*]] = alloca i32 + + // Initialize. + // CHECK-NEXT: [[E_BEGIN:%.*]] = getelementptr inbounds [10 x [[A]]]* [[AS]], i64 0, i64 0 + // CHECK-NEXT: store [[A]]* [[E_BEGIN]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: invoke void @_ZN5test01AC1Ei([[A]]* [[E_BEGIN]], i32 5) + // CHECK: [[E1:%.*]] = getelementptr inbounds [[A]]* [[E_BEGIN]], i64 1 + // CHECK-NEXT: store [[A]]* [[E1]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: invoke void @_ZN5test01AC1Ei([[A]]* [[E1]], i32 7) + // CHECK: [[E2:%.*]] = getelementptr inbounds [[A]]* [[E1]], i64 1 + // CHECK-NEXT: store [[A]]* [[E2]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: [[E_END:%.*]] = getelementptr inbounds [[A]]* [[E_BEGIN]], i64 10 + // CHECK-NEXT: br label + // CHECK: [[E_CUR:%.*]] = phi [[A]]* [ [[E2]], {{%.*}} ], [ [[E_NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: invoke void @_ZN5test01AC1Ev([[A]]* [[E_CUR]]) + // CHECK: [[E_NEXT]] = getelementptr inbounds [[A]]* [[E_CUR]], i64 1 + // CHECK-NEXT: store [[A]]* [[E_NEXT]], [[A]]** [[ENDVAR]] + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[E_NEXT]], [[E_END]] + // CHECK-NEXT: br i1 [[T0]], + + // Run. + // CHECK: invoke void @_Z6opaquev() + + // Normal destroy. + // CHECK: [[ED_BEGIN:%.*]] = getelementptr inbounds [10 x [[A]]]* [[AS]], i32 0, i32 0 + // CHECK-NEXT: [[ED_END:%.*]] = getelementptr inbounds [[A]]* [[ED_BEGIN]], i64 10 + // CHECK-NEXT: br label + // CHECK: [[ED_AFTER:%.*]] = phi [[A]]* [ [[ED_END]], {{%.*}} ], [ [[ED_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[ED_CUR]] = getelementptr inbounds [[A]]* [[ED_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[ED_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[ED_CUR]], [[ED_BEGIN]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: ret void + + // Partial destroy for initialization. + // CHECK: llvm.eh.selector({{.*}}, i32 0) + // CHECK: [[PARTIAL_END:%.*]] = load [[A]]** [[ENDVAR]] + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[E_BEGIN]], [[PARTIAL_END]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[E_AFTER:%.*]] = phi [[A]]* [ [[PARTIAL_END]], {{%.*}} ], [ [[E_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[E_CUR]] = getelementptr inbounds [[A]]* [[E_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[E_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[E_CUR]], [[E_BEGIN]] + // CHECK-NEXT: br i1 [[T0]], + + // Primary EH destructor. + // CHECK: llvm.eh.selector({{.*}}, i32 0) + // CHECK: [[E0:%.*]] = getelementptr inbounds [10 x [[A]]]* [[AS]], i32 0, i32 0 + // CHECK-NEXT: [[E_END:%.*]] = getelementptr inbounds [[A]]* [[E0]], i64 10 + // CHECK-NEXT: br label + + // Partial destructor for primary normal destructor. + // FIXME: There's some really bad block ordering here which causes + // the partial destroy for the primary normal destructor to fall + // within the primary EH destructor. + // CHECK: llvm.eh.selector({{.*}}, i32 0) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[ED_BEGIN]], [[ED_CUR]] + // CHECK-NEXT: br i1 [[T0]] + // CHECK: [[EDD_AFTER:%.*]] = phi [[A]]* [ [[ED_CUR]], {{%.*}} ], [ [[EDD_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[EDD_CUR]] = getelementptr inbounds [[A]]* [[EDD_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[EDD_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[EDD_CUR]], [[ED_BEGIN]] + // CHECK-NEXT: br i1 [[T0]] + + // Back to the primary EH destructor. + // CHECK: [[E_AFTER:%.*]] = phi [[A]]* [ [[E_END]], {{%.*}} ], [ [[E_CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[E_CUR]] = getelementptr inbounds [[A]]* [[E_AFTER]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test01AD1Ev([[A]]* [[E_CUR]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[E_CUR]], [[E0]] + // CHECK-NEXT: br i1 [[T0]], + +} + +namespace test1 { + struct A { A(); A(int); ~A(); }; + struct B { A x, y, z; int w; }; + + void test() { + B v = { 5, 6, 7, 8 }; + } + // CHECK: define void @_ZN5test14testEv() + // CHECK: [[V:%.*]] = alloca [[B:%.*]], align 4 + // CHECK-NEXT: alloca i8* + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: [[X:%.*]] = getelementptr inbounds [[B]]* [[V]], i32 0, i32 0 + // CHECK-NEXT: call void @_ZN5test11AC1Ei([[A:%.*]]* [[X]], i32 5) + // CHECK-NEXT: [[Y:%.*]] = getelementptr inbounds [[B]]* [[V]], i32 0, i32 1 + // CHECK-NEXT: invoke void @_ZN5test11AC1Ei([[A]]* [[Y]], i32 6) + // CHECK: [[Z:%.*]] = getelementptr inbounds [[B]]* [[V]], i32 0, i32 2 + // CHECK-NEXT: invoke void @_ZN5test11AC1Ei([[A]]* [[Z]], i32 7) + // CHECK: [[W:%.*]] = getelementptr inbounds [[B]]* [[V]], i32 0, i32 3 + // CHECK-NEXT: store i32 8, i32* [[W]], align 4 + // CHECK-NEXT: call void @_ZN5test11BD1Ev([[B]]* [[V]]) + // CHECK-NEXT: ret void + + // FIXME: again, the block ordering is pretty bad here + // CHECK: eh.selector({{.*}}, i32 0) + // CHECK: eh.selector({{.*}}, i32 0) + // CHECK: invoke void @_ZN5test11AD1Ev([[A]]* [[Y]]) + // CHECK: invoke void @_ZN5test11AD1Ev([[A]]* [[X]]) +} + +namespace test2 { + struct A { A(); ~A(); }; + + void test() { + A v[4][7]; + + // CHECK: define void @_ZN5test24testEv() + // CHECK: [[V:%.*]] = alloca [4 x [7 x [[A:%.*]]]], align 1 + // CHECK-NEXT: alloca i8* + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: alloca i32 + + // Main initialization loop. + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [4 x [7 x [[A]]]]* [[V]], i32 0, i32 0, i32 0 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 28 + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: invoke void @_ZN5test21AC1Ev([[A]]* [[CUR]]) + // CHECK: [[NEXT:%.*]] = getelementptr inbounds [[A]]* [[CUR]], i64 1 + // CHECK-NEXT: [[DONE:%.*]] = icmp eq [[A]]* [[NEXT]], [[END]] + // CHECK-NEXT: br i1 [[DONE]], + + // Partial destruction landing pad. + // CHECK: llvm.eh.exception() + // CHECK: [[EMPTY:%.*]] = icmp eq [[A]]* [[BEGIN]], [[CUR]] + // CHECK-NEXT: br i1 [[EMPTY]], + // CHECK: [[PAST:%.*]] = phi [[A]]* [ [[CUR]], {{%.*}} ], [ [[DEL:%.*]], {{%.*}} ] + // CHECK-NEXT: [[DEL]] = getelementptr inbounds [[A]]* [[PAST]], i64 -1 + // CHECK-NEXT: invoke void @_ZN5test21AD1Ev([[A]]* [[DEL]]) + // CHECK: [[T0:%.*]] = icmp eq [[A]]* [[DEL]], [[BEGIN]] + // CHECK-NEXT: br i1 [[T0]], + } + +} + +// PR10351 +namespace test3 { + struct A { A(); ~A(); void *p; }; + struct B { + B() {} + A a; + }; + + B *test() { + return new B[10]; + // invoke void @_ZN5test31BD1Ev( + } +} diff --git a/test/CodeGenCXX/pointers-to-data-members.cpp b/test/CodeGenCXX/pointers-to-data-members.cpp index 2a22d23e509d..f0246c89b699 100644 --- a/test/CodeGenCXX/pointers-to-data-members.cpp +++ b/test/CodeGenCXX/pointers-to-data-members.cpp @@ -35,7 +35,7 @@ namespace ZeroInit { } ssa[2]; void test_ssa() { (void) ssa; } - // CHECK-GLOBAL: @_ZN8ZeroInit2ssE = internal global %1 { %struct.anon { i64 -1 } } + // CHECK-GLOBAL: @_ZN8ZeroInit2ssE = internal global %struct.anon.1 { %struct.anon.2 { i64 -1 } } struct { struct { int A::*pa; @@ -55,7 +55,7 @@ namespace ZeroInit { }; struct C : A, B { int j; }; - // CHECK-GLOBAL: @_ZN8ZeroInit1cE = global {{%.*}} { %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.ZeroInit::B" { [10 x %"struct.PR7139::ptr_to_member_struct"] [%"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }, %"struct.PR7139::ptr_to_member_struct" { i64 -1, i32 0 }], i8 0, i64 -1 }, i32 0 }, align 8 + // CHECK-GLOBAL: @_ZN8ZeroInit1cE = global {{%.*}} { %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::B" { [10 x %"struct.ZeroInit::A"] [%"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }, %"struct.ZeroInit::A" { i64 -1, i32 0 }], i8 0, i64 -1 }, i32 0 }, align 8 C c; } @@ -227,6 +227,6 @@ namespace test4 { struct C : virtual B { int *C_p; }; struct D : C { int *D_p; }; - // CHECK-GLOBAL: @_ZN5test41dE = global %"struct.test4::D" { %"struct.test4::C.base" zeroinitializer, i32* null, %"struct.VirtualBases::C.base" { i32 (...)** null, i64 -1 }, %"struct.test4::A" zeroinitializer }, align 8 + // CHECK-GLOBAL: @_ZN5test41dE = global %"struct.test4::D" { %"struct.test4::C.base" zeroinitializer, i32* null, %"struct.test4::B.base" { i32 (...)** null, i64 -1 }, %"struct.test4::A" zeroinitializer }, align 8 D d; } diff --git a/test/CodeGenCXX/pr9965.cpp b/test/CodeGenCXX/pr9965.cpp index b87fcf42023b..596dee9caaa3 100644 --- a/test/CodeGenCXX/pr9965.cpp +++ b/test/CodeGenCXX/pr9965.cpp @@ -7,6 +7,6 @@ struct X X<int> x; // CHECK: define internal void @__cxx_global_var_init() -// CHECK: call void @_ZN1XIiEC1Ev -// CHECK: define linkonce_odr void @_ZN1XIiEC1Ev -// CHECK: define linkonce_odr void @_ZN1XIiEC2Ev +// CHECK: call {{.*}} @_ZN1XIiEC1Ev +// CHECK: define linkonce_odr {{.*}} @_ZN1XIiEC1Ev +// CHECK: define linkonce_odr {{.*}} @_ZN1XIiEC2Ev diff --git a/test/CodeGenCXX/references.cpp b/test/CodeGenCXX/references.cpp index 25bc8d801790..4bbc251ab5d0 100644 --- a/test/CodeGenCXX/references.cpp +++ b/test/CodeGenCXX/references.cpp @@ -235,7 +235,7 @@ struct A { }; // CHECK: define internal void @__cxx_global_var_init -// CHECK: call void @_ZN2N31AC1Ei(%"class.N2::X"* @_ZGRN2N35sA123E, i32 123) +// CHECK: call void @_ZN2N31AC1Ei(%"struct.N3::A"* @_ZGRN2N35sA123E, i32 123) // CHECK: call i32 @__cxa_atexit // CHECK: ret void const A &sA123 = A(123); @@ -250,7 +250,7 @@ struct A { void f() { // CHECK: define void @_ZN2N41fEv - // CHECK: call void @_ZN2N41AC1Ev(%"class.N2::X"* @_ZGRZN2N41fEvE2ar) + // CHECK: call void @_ZN2N41AC1Ev(%"struct.N4::A"* @_ZGRZN2N41fEvE2ar) // CHECK: call i32 @__cxa_atexit // CHECK: ret void static const A& ar = A(); @@ -269,3 +269,31 @@ void h() { f(g().b); } } + +// PR9565 +namespace PR9565 { + struct a { int a : 10, b : 10; }; + // CHECK: define void @_ZN6PR95651fEv() + void f() { + // CHECK: call void @llvm.memcpy + a x = { 0, 0 }; + // CHECK: [[WITH_SEVENTEEN:%[a-zA-Z0-9]+]] = or i32 [[WITHOUT_SEVENTEEN:%[a-zA-Z0-9]+]], 17 + // CHECK: store i32 [[WITH_SEVENTEEN]], i32* [[XA:%[a-zA-Z0-9]+]] + x.a = 17; + // CHECK-NEXT: bitcast + // CHECK-NEXT: load + // CHECK-NEXT: and + // CHECK-NEXT: shl + // CHECK-NEXT: ashr + // CHECK-NEXT: store i32 + // CHECK-NEXT: store i32* + const int &y = x.a; + // CHECK-NEXT: bitcast + // CHECK-NEXT: load + // CHECK-NEXT: and + // CHECK-NEXT: or + // CHECK-NEXT: store i32 + x.b = 19; + // CHECK-NEXT: ret void + } +} diff --git a/test/CodeGenCXX/static-init-3.cpp b/test/CodeGenCXX/static-init-3.cpp index bd717caaa172..dc28d5a32a63 100644 --- a/test/CodeGenCXX/static-init-3.cpp +++ b/test/CodeGenCXX/static-init-3.cpp @@ -16,8 +16,8 @@ struct X1 } }; -// CHECK: @_ZN2X1I2X2I1BEE8instanceE = weak_odr global %struct.X0* null, align 8 -// CHECJ: @_ZN2X1I2X2I1AEE8instanceE = weak_odr global %struct.X0* null, align 8 +// CHECK: @_ZN2X1I2X2I1BEE8instanceE = weak_odr global %struct.X2* null, align 8 +// CHECJ: @_ZN2X1I2X2I1AEE8instanceE = weak_odr global %struct.X2* null, align 8 template<class T> T & X1<T>::instance = X1<T>::get(); class A { }; diff --git a/test/CodeGenCXX/static-init.cpp b/test/CodeGenCXX/static-init.cpp index 78749a7a2faf..d488e636696e 100644 --- a/test/CodeGenCXX/static-init.cpp +++ b/test/CodeGenCXX/static-init.cpp @@ -16,6 +16,9 @@ void f() { // CHECK: call void @_ZN1AC1Ev // CHECK: call i32 @__cxa_atexit(void (i8*)* bitcast (void (%struct.A*)* @_ZN1AD1Ev to void (i8*)*), i8* getelementptr inbounds (%struct.A* @_ZZ1fvE1a, i32 0, i32 0), i8* bitcast (i8** @__dso_handle to i8*)) // CHECK: call void @__cxa_guard_release + + // rdar://problem/9496726 + // CHECK: call void @llvm.memory.barrier(i1 true, i1 true, i1 false, i1 false, i1 false) static A a; } diff --git a/test/CodeGenCXX/stmtexpr.cpp b/test/CodeGenCXX/stmtexpr.cpp index 0828d592fec1..1f0e4ac4bf29 100644 --- a/test/CodeGenCXX/stmtexpr.cpp +++ b/test/CodeGenCXX/stmtexpr.cpp @@ -46,11 +46,11 @@ void foo3() void foo4() { -// CHECK: call void @_ZN1AC1Ei -// CHECK: call void @_ZN1AC1ERKS_ -// CHECK: call void @_ZN1AD1Ev -// CHECK: call void @_ZN1BC1ERK1A -// CHECK: call void @_ZN1AD1Ev +// CHECK: call {{.*}} @_ZN1AC1Ei +// CHECK: call {{.*}} @_ZN1AC1ERKS_ +// CHECK: call {{.*}} @_ZN1AD1Ev +// CHECK: call {{.*}} @_ZN1BC1ERK1A +// CHECK: call {{.*}} @_ZN1AD1Ev const B &b = ({ A a(1); a; }); if (b.i != 1) abort(); diff --git a/test/CodeGenCXX/template-anonymous-types.cpp b/test/CodeGenCXX/template-anonymous-types.cpp index 68bdc0c4a47d..72fe090ceb7f 100644 --- a/test/CodeGenCXX/template-anonymous-types.cpp +++ b/test/CodeGenCXX/template-anonymous-types.cpp @@ -32,6 +32,6 @@ void test() { // CHECK: define internal void @"_ZN1XIN1S3$_1EEC2ES1_"(%struct.X* %this, i32 %t) unnamed_addr // // FOO's instantiation of X: - // CHECK: define internal i32 @"_ZN1XIN1S3$_0EE1fEv"(%struct.X* %this) - // CHECK: define internal void @"_ZN1XIN1S3$_0EEC2ES1_"(%struct.X* %this, i32 %t) unnamed_addr + // CHECK: define internal i32 @"_ZN1XIN1S3$_0EE1fEv"(%struct.X.0* %this) + // CHECK: define internal void @"_ZN1XIN1S3$_0EEC2ES1_"(%struct.X.0* %this, i32 %t) unnamed_addr } diff --git a/test/CodeGenCXX/temporaries.cpp b/test/CodeGenCXX/temporaries.cpp index 348d51e019a6..8aeca653da55 100644 --- a/test/CodeGenCXX/temporaries.cpp +++ b/test/CodeGenCXX/temporaries.cpp @@ -421,28 +421,24 @@ namespace Elision { void test4() { // CHECK: [[X:%.*]] = alloca [[A]], align 8 // CHECK-NEXT: [[XS:%.*]] = alloca [2 x [[A]]], align 16 - // CHECK-NEXT: [[I:%.*]] = alloca i64 // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[X]]) A x; - // CHECK-NEXT: [[XS0:%.*]] = getelementptr inbounds [2 x [[A]]]* [[XS]], i32 0, i32 0 + // CHECK-NEXT: [[XS0:%.*]] = getelementptr inbounds [2 x [[A]]]* [[XS]], i64 0, i64 0 // CHECK-NEXT: call void @_ZN7Elision1AC1Ev([[A]]* [[XS0]]) - // CHECK-NEXT: [[XS1:%.*]] = getelementptr inbounds [2 x [[A]]]* [[XS]], i32 0, i32 1 + // CHECK-NEXT: [[XS1:%.*]] = getelementptr inbounds [[A]]* [[XS0]], i64 1 // CHECK-NEXT: call void @_ZN7Elision1AC1ERKS0_([[A]]* [[XS1]], [[A]]* [[X]]) - // CHECK-NEXT: [[XSB:%.*]] = bitcast [2 x [[A]]]* [[XS]] to [[A]]* A xs[] = { A(), x }; - // CHECK-NEXT: store i64 2, i64* [[I]] - // CHECK-NEXT: br label - // CHECK: [[I0:%.*]] = load i64* [[I]] - // CHECK-NEXT: icmp ne i64 [[I0]], 0 - // CHECK-NEXT: br i1 - // CHECK: [[I1:%.*]] = load i64* [[I]] - // CHECK-NEXT: [[I2:%.*]] = sub i64 [[I1]], 1 - // CHECK-NEXT: [[XSI:%.*]] = getelementptr inbounds [[A]]* [[XSB]], i64 [[I2]] - // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[XSI]]) + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [2 x [[A]]]* [[XS]], i32 0, i32 0 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[BEGIN]], i64 2 // CHECK-NEXT: br label + // CHECK: [[AFTER:%.*]] = phi [[A]]* + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds [[A]]* [[AFTER]], i64 -1 + // CHECK-NEXT: call void @_ZN7Elision1AD1Ev([[A]]* [[CUR]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[T0]], // CHECK: call void @_ZN7Elision1AD1Ev([[A]]* [[X]]) } diff --git a/test/CodeGenCXX/value-init.cpp b/test/CodeGenCXX/value-init.cpp index a5a0b67d57d8..04a18b3fa801 100644 --- a/test/CodeGenCXX/value-init.cpp +++ b/test/CodeGenCXX/value-init.cpp @@ -105,34 +105,24 @@ void f() { // CHECK: call void @_ZN6PR98014TestC1Ei // CHECK-NOT: call void @llvm.memset.p0i8.i64 // CHECK: call void @_ZN6PR98014TestC1Ev - // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98014TestC1Ev Test partial[3] = { 1 }; // CHECK-NOT: call void @llvm.memset.p0i8.i64 // CHECK: call void @_ZN6PR98014TestC1Ev - // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98014TestC1Ev - // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98014TestC1Ev + // CHECK-NOT: call void @_ZN6PR98014TestC1Ev Test empty[3] = {}; // CHECK: call void @llvm.memset.p0i8.i64 // CHECK-NOT: call void @llvm.memset.p0i8.i64 // CHECK: call void @_ZN6PR98015Test2C1Ev - // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98015Test2C1Ev - // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98015Test2C1Ev + // CHECK-NOT: call void @_ZN6PR98015Test2C1Ev Test2 empty2[3] = {}; // CHECK: call void @llvm.memset.p0i8.i64 // CHECK-NOT: call void @llvm.memset.p0i8.i64 // CHECK: call void @_ZN6PR98015Test3C1Ev // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98015Test3C1Ev - // CHECK-NOT: call void @llvm.memset.p0i8.i64 - // CHECK: call void @_ZN6PR98015Test3C1Ev + // CHECK-NOT: call void @_ZN6PR98015Test3C1Ev Test3 empty3[3] = {}; } @@ -189,10 +179,7 @@ namespace zeroinit { X3<int>().f(); } - // CHECK: define linkonce_odr void @_ZN8zeroinit2X3IiEC2Ev(%"struct.zeroinit::X3"* %this) unnamed_addr - // CHECK: call void @llvm.memset.p0i8.i64 - // CHECK-NEXT: call void @_ZN8zeroinit2X2IiEC2Ev - // CHECK-NEXT: ret void + // More checks at EOF } namespace PR8726 { @@ -207,3 +194,51 @@ void f(const C& c) { } } + +// rdar://problem/9355931 +namespace test6 { + struct A { A(); A(int); }; + + void test() { + A arr[10][20] = { 5 }; + }; + // CHECK: define void @_ZN5test64testEv() + // CHECK: [[ARR:%.*]] = alloca [10 x [20 x [[A:%.*]]]], + + // CHECK-NEXT: [[INNER:%.*]] = getelementptr inbounds [10 x [20 x [[A]]]]* [[ARR]], i64 0, i64 0 + // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [20 x [[A]]]* [[INNER]], i64 0, i64 0 + // CHECK-NEXT: call void @_ZN5test61AC1Ei([[A]]* [[T0]], i32 5) + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [[A]]* [[T0]], i64 1 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [[A]]* [[T0]], i64 20 + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [[A]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: call void @_ZN5test61AC1Ev([[A]]* [[CUR]]) + // CHECK-NEXT: [[NEXT]] = getelementptr inbounds [[A]]* [[CUR]], i64 1 + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[NEXT]], [[END]] + // CHECK-NEXT: br i1 + + // CHECK: [[BEGIN:%.*]] = getelementptr inbounds [20 x [[A]]]* [[INNER]], i64 1 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds [20 x [[A]]]* [[INNER]], i64 10 + // CHECK-NEXT: br label + // CHECK: [[CUR:%.*]] = phi [20 x [[A]]]* [ [[BEGIN]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + + // Inner loop. + // CHECK-NEXT: [[IBEGIN:%.*]] = getelementptr inbounds [20 x [[A]]]* [[CUR]], i32 0, i32 0 + // CHECK-NEXT: [[IEND:%.*]] = getelementptr inbounds [[A]]* [[IBEGIN]], i64 20 + // CHECK-NEXT: br label + // CHECK: [[ICUR:%.*]] = phi [[A]]* [ [[IBEGIN]], {{%.*}} ], [ [[INEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: call void @_ZN5test61AC1Ev([[A]]* [[ICUR]]) + // CHECK-NEXT: [[INEXT:%.*]] = getelementptr inbounds [[A]]* [[ICUR]], i64 1 + // CHECK-NEXT: [[T0:%.*]] = icmp eq [[A]]* [[INEXT]], [[IEND]] + // CHECK-NEXT: br i1 [[T0]], + + // CHECK: [[NEXT]] = getelementptr inbounds [20 x [[A]]]* [[CUR]], i64 1 + // CHECK-NEXT: [[T0:%.*]] = icmp eq [20 x [[A]]]* [[NEXT]], [[END]] + // CHECK-NEXT: br i1 [[T0]] + // CHECK: ret void +} + +// CHECK: define linkonce_odr void @_ZN8zeroinit2X3IiEC2Ev(%"struct.zeroinit::X3"* %this) unnamed_addr +// CHECK: call void @llvm.memset.p0i8.i64 +// CHECK-NEXT: call void @_ZN8zeroinit2X2IiEC2Ev +// CHECK-NEXT: ret void diff --git a/test/CodeGenCXX/variadic-templates.cpp b/test/CodeGenCXX/variadic-templates.cpp index 4f3cf1fe9db6..90c837067559 100644 --- a/test/CodeGenCXX/variadic-templates.cpp +++ b/test/CodeGenCXX/variadic-templates.cpp @@ -9,4 +9,15 @@ int get_num_types(Types...) { // CHECK: ret i32 3 template int get_num_types(int, float, double); +// PR10260 - argument packs that expand to nothing +namespace test1 { + template <class... T> void foo() { + int values[sizeof...(T)+1] = { T::value... }; + // CHECK: define linkonce_odr void @_ZN5test13fooIJEEEvv() + // CHECK: alloca [1 x i32], align 4 + } + void test() { + foo<>(); + } +} diff --git a/test/CodeGenCXX/virt-call-offsets.cpp b/test/CodeGenCXX/virt-call-offsets.cpp index 3eb6b5da7d0c..5eef6fea9e6f 100644 --- a/test/CodeGenCXX/virt-call-offsets.cpp +++ b/test/CodeGenCXX/virt-call-offsets.cpp @@ -5,4 +5,4 @@ struct B : A {}; struct C : B { virtual void a(); }; void (C::*x)() = &C::a; -// CHECK: @x = global %0 { i{{[0-9]+}} 1, i{{[0-9]+}} 0 } +// CHECK: @x = global { i{{[0-9]+}}, i{{[0-9]+}} } { i{{[0-9]+}} 1, i{{[0-9]+}} 0 } diff --git a/test/CodeGenCXX/virtual-base-destructor-call.cpp b/test/CodeGenCXX/virtual-base-destructor-call.cpp index f028e4c733fe..2424d218d6b7 100644 --- a/test/CodeGenCXX/virtual-base-destructor-call.cpp +++ b/test/CodeGenCXX/virtual-base-destructor-call.cpp @@ -18,34 +18,34 @@ int main() { // basic_iostream's complete dtor calls its base dtor, then its // virtual base's dtor. -// CHECK: define linkonce_odr void @_ZN14basic_iostreamIcED1Ev(%struct.basic_iostream* %this) unnamed_addr -// CHECK: call void @_ZN14basic_iostreamIcED2Ev -// CHECK: call void @_ZN9basic_iosD2Ev +// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED1Ev(%struct.basic_iostream* %this) unnamed_addr +// CHECK: call {{.*}} @_ZN14basic_iostreamIcED2Ev +// CHECK: call {{.*}} @_ZN9basic_iosD2Ev // basic_iostream's base dtor calls its non-virtual base dtor. -// CHECK: define linkonce_odr void @_ZN14basic_iostreamIcED2Ev(%struct.basic_iostream* %this, i8** %vtt) unnamed_addr -// CHECK: call void @_ZN13basic_istreamIcED2Ev +// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED2Ev(%struct.basic_iostream* %this, i8** %vtt) unnamed_addr +// CHECK: call {{.*}} @_ZN13basic_istreamIcED2Ev // CHECK: } // basic_iostream's deleting dtor calls its complete dtor, then // operator delete(). -// CHECK: define linkonce_odr void @_ZN14basic_iostreamIcED0Ev(%struct.basic_iostream* %this) unnamed_addr -// CHECK: call void @_ZN14basic_iostreamIcED1Ev -// CHECK: call void @_ZdlPv +// CHECK: define linkonce_odr {{.*}} @_ZN14basic_iostreamIcED0Ev(%struct.basic_iostream* %this) unnamed_addr +// CHECK: call {{.*}} @_ZN14basic_iostreamIcED1Ev +// CHECK: call {{.*}} @_ZdlPv // basic_istream's complete dtor calls the base dtor, // then its virtual base's base dtor. -// CHECK: define linkonce_odr void @_ZN13basic_istreamIcED1Ev(%struct.basic_istream* %this) unnamed_addr -// CHECK: call void @_ZN13basic_istreamIcED2Ev -// CHECK: call void @_ZN9basic_iosD2Ev +// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED1Ev(%struct.basic_istream* %this) unnamed_addr +// CHECK: call {{.*}} @_ZN13basic_istreamIcED2Ev +// CHECK: call {{.*}} @_ZN9basic_iosD2Ev // basic_istream's deleting dtor calls the complete dtor, then // operator delete(). -// CHECK: define linkonce_odr void @_ZN13basic_istreamIcED0Ev(%struct.basic_istream* %this) unnamed_addr -// CHECK: call void @_ZN13basic_istreamIcED1Ev -// CHECK: call void @_ZdlPv +// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED0Ev(%struct.basic_istream* %this) unnamed_addr +// CHECK: call {{.*}} @_ZN13basic_istreamIcED1Ev +// CHECK: call {{.*}} @_ZdlPv // basic_istream's base dtor is a no-op. -// CHECK: define linkonce_odr void @_ZN13basic_istreamIcED2Ev(%struct.basic_istream* %this, i8** %vtt) unnamed_addr +// CHECK: define linkonce_odr {{.*}} @_ZN13basic_istreamIcED2Ev(%struct.basic_istream* %this, i8** %vtt) unnamed_addr // CHECK-NOT: call // CHECK: } diff --git a/test/CodeGenCXX/virtual-bases.cpp b/test/CodeGenCXX/virtual-bases.cpp index cfb4c837162e..c9f13f853d1b 100644 --- a/test/CodeGenCXX/virtual-bases.cpp +++ b/test/CodeGenCXX/virtual-bases.cpp @@ -20,8 +20,8 @@ struct C : virtual A { C(bool); }; -// CHECK: define void @_ZN1CC1Eb(%struct.B* %this, i1 zeroext) unnamed_addr -// CHECK: define void @_ZN1CC2Eb(%struct.B* %this, i8** %vtt, i1 zeroext) unnamed_addr +// CHECK: define void @_ZN1CC1Eb(%struct.C* %this, i1 zeroext) unnamed_addr +// CHECK: define void @_ZN1CC2Eb(%struct.C* %this, i8** %vtt, i1 zeroext) unnamed_addr C::C(bool) { } // PR6251 diff --git a/test/CodeGenCXX/virtual-functions-incomplete-types.cpp b/test/CodeGenCXX/virtual-functions-incomplete-types.cpp index 052a0192d665..afa658f7d374 100644 --- a/test/CodeGenCXX/virtual-functions-incomplete-types.cpp +++ b/test/CodeGenCXX/virtual-functions-incomplete-types.cpp @@ -9,7 +9,7 @@ struct B { void B::f() { } -// CHECK: define i32 @_ZN1D1gEv(%struct.B* %this) +// CHECK: define i32 @_ZN1D1gEv(%struct.D* %this) // CHECK: declare void @_ZN1B1gEv() struct C; diff --git a/test/CodeGenCXX/virtual-pseudo-destructor-call.cpp b/test/CodeGenCXX/virtual-pseudo-destructor-call.cpp index 285e3da75924..0d3574e46d33 100644 --- a/test/CodeGenCXX/virtual-pseudo-destructor-call.cpp +++ b/test/CodeGenCXX/virtual-pseudo-destructor-call.cpp @@ -5,6 +5,10 @@ struct A { }; void f(A *a) { - // CHECK: call void % + // CHECK: define {{.*}} @_Z1fP1A + // CHECK: load + // CHECK: load + // CHECK: [[CALLEE:%[a-zA-Z0-9.]*]] = load + // CHECK: call {{.*}} [[CALLEE]]( a->~A(); } diff --git a/test/CodeGenCXX/visibility-inlines-hidden.cpp b/test/CodeGenCXX/visibility-inlines-hidden.cpp index 760879a2a63c..f7fabed8c10a 100644 --- a/test/CodeGenCXX/visibility-inlines-hidden.cpp +++ b/test/CodeGenCXX/visibility-inlines-hidden.cpp @@ -77,7 +77,7 @@ namespace test1 { a.foo(); } // CHECK: declare void @_ZN5test11A3fooEv -// CHECK: declare void @_ZN5test11AD1Ev +// CHECK: declare {{.*}} @_ZN5test11AD1Ev } // PR8713 diff --git a/test/CodeGenCXX/visibility.cpp b/test/CodeGenCXX/visibility.cpp index 7644e47ff780..fdccd4604545 100644 --- a/test/CodeGenCXX/visibility.cpp +++ b/test/CodeGenCXX/visibility.cpp @@ -422,3 +422,35 @@ namespace test21 { // CHECK: define weak_odr void @_ZN6test211AILNS_2EnE0EE3fooEv( template void A<en>::foo(); } + +// rdar://problem/9616154 +// Visibility on explicit specializations should take precedence. +namespace test22 { + class A1 {}; + class A2 {}; + + template <class T> struct B {}; + template <> struct DEFAULT B<A1> { + static void foo(); + static void bar() {} + }; + template <> struct B<A2> { + static void foo(); + static void bar() {} + }; + + void test() { + B<A1>::foo(); + B<A1>::bar(); + B<A2>::foo(); + B<A2>::bar(); + } + // CHECK: declare void @_ZN6test221BINS_2A1EE3fooEv() + // CHECK: define linkonce_odr void @_ZN6test221BINS_2A1EE3barEv() + // CHECK: declare void @_ZN6test221BINS_2A2EE3fooEv() + // CHECK: define linkonce_odr void @_ZN6test221BINS_2A2EE3barEv() + // CHECK-HIDDEN: declare void @_ZN6test221BINS_2A1EE3fooEv() + // CHECK-HIDDEN: define linkonce_odr void @_ZN6test221BINS_2A1EE3barEv() + // CHECK-HIDDEN: declare void @_ZN6test221BINS_2A2EE3fooEv() + // CHECK-HIDDEN: define linkonce_odr hidden void @_ZN6test221BINS_2A2EE3barEv() +} diff --git a/test/CodeGenCXX/vla.cpp b/test/CodeGenCXX/vla.cpp new file mode 100644 index 000000000000..58cdf795ee5a --- /dev/null +++ b/test/CodeGenCXX/vla.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -o - | FileCheck %s + +// rdar://problem/9506377 +void test0(void *array, int n) { + // CHECK: define void @_Z5test0Pvi( + // CHECK: [[ARRAY:%.*]] = alloca i8*, align 8 + // CHECK-NEXT: [[N:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[REF:%.*]] = alloca i16*, align 8 + // CHECK-NEXT: [[S:%.*]] = alloca i16, align 2 + // CHECK-NEXT: store i8* + // CHECK-NEXT: store i32 + + // Capture the bounds. + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[DIM0:%.*]] = zext i32 [[T0]] to i64 + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[T1:%.*]] = add nsw i32 [[T0]], 1 + // CHECK-NEXT: [[DIM1:%.*]] = zext i32 [[T1]] to i64 + typedef short array_t[n][n+1]; + + // CHECK-NEXT: [[T0:%.*]] = load i8** [[ARRAY]], align 8 + // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i16* + // CHECK-NEXT: store i16* [[T1]], i16** [[REF]], align 8 + array_t &ref = *(array_t*) array; + + // CHECK-NEXT: [[T0:%.*]] = load i16** [[REF]] + // CHECK-NEXT: [[T1:%.*]] = mul nsw i64 1, [[DIM1]] + // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i16* [[T0]], i64 [[T1]] + // CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i16* [[T2]], i64 2 + // CHECK-NEXT: store i16 3, i16* [[T3]] + ref[1][2] = 3; + + // CHECK-NEXT: [[T0:%.*]] = load i16** [[REF]] + // CHECK-NEXT: [[T1:%.*]] = mul nsw i64 4, [[DIM1]] + // CHECK-NEXT: [[T2:%.*]] = getelementptr inbounds i16* [[T0]], i64 [[T1]] + // CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i16* [[T2]], i64 5 + // CHECK-NEXT: [[T4:%.*]] = load i16* [[T3]] + // CHECK-NEXT: store i16 [[T4]], i16* [[S]], align 2 + short s = ref[4][5]; + + // CHECK-NEXT: ret void +} + diff --git a/test/CodeGenCXX/volatile-1.cpp b/test/CodeGenCXX/volatile-1.cpp index 3ae17bd41b75..1a69648d42a7 100644 --- a/test/CodeGenCXX/volatile-1.cpp +++ b/test/CodeGenCXX/volatile-1.cpp @@ -4,7 +4,7 @@ volatile int i, j, k; volatile int ar[5]; volatile char c; -// CHECK: @ci = global [[CINT:%.*]] zeroinitializer +// CHECK: @ci = global [[CINT:.*]] zeroinitializer volatile _Complex int ci; volatile struct S { #ifdef __cplusplus diff --git a/test/CodeGenCXX/vtable-pointer-initialization.cpp b/test/CodeGenCXX/vtable-pointer-initialization.cpp index f629c2db79ca..9b1eaa5ba775 100644 --- a/test/CodeGenCXX/vtable-pointer-initialization.cpp +++ b/test/CodeGenCXX/vtable-pointer-initialization.cpp @@ -41,16 +41,16 @@ struct B : Base { void f() { B b; } -// CHECK: define linkonce_odr void @_ZN1BC1Ev(%struct.A* %this) unnamed_addr +// CHECK: define linkonce_odr void @_ZN1BC1Ev(%struct.B* %this) unnamed_addr // CHECK: call void @_ZN1BC2Ev( -// CHECK: define linkonce_odr void @_ZN1BD1Ev(%struct.A* %this) unnamed_addr +// CHECK: define linkonce_odr void @_ZN1BD1Ev(%struct.B* %this) unnamed_addr // CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1B, i64 0, i64 2) // CHECK: call void @_ZN5FieldD1Ev( // CHECK: call void @_ZN4BaseD2Ev( // CHECK: ret void -// CHECK: define linkonce_odr void @_ZN1BC2Ev(%struct.A* %this) unnamed_addr +// CHECK: define linkonce_odr void @_ZN1BC2Ev(%struct.B* %this) unnamed_addr // CHECK: call void @_ZN4BaseC2Ev( // CHECK: store i8** getelementptr inbounds ([3 x i8*]* @_ZTV1B, i64 0, i64 2) // CHECK: call void @_ZN5FieldC1Ev diff --git a/test/CodeGenCXX/x86_32-arguments.cpp b/test/CodeGenCXX/x86_32-arguments.cpp index 4c0603346f05..4d4339381dbe 100644 --- a/test/CodeGenCXX/x86_32-arguments.cpp +++ b/test/CodeGenCXX/x86_32-arguments.cpp @@ -89,7 +89,7 @@ struct s5 { s5(); int &x; }; s5 f5() { return s5(); } // CHECK: define i32 @_Z4f6_0M2s6i(i32 %a) -// CHECK: define i64 @_Z4f6_1M2s6FivE(%{{.*}} byval align 4) +// CHECK: define i64 @_Z4f6_1M2s6FivE({ i32, i32 }* byval align 4) // FIXME: It would be nice to avoid byval on the previous case. struct s6 {}; typedef int s6::* s6_mdp; diff --git a/test/CodeGenCXX/x86_64-arguments.cpp b/test/CodeGenCXX/x86_64-arguments.cpp index 01f1a445b1c7..e1d9dfc1fc30 100644 --- a/test/CodeGenCXX/x86_64-arguments.cpp +++ b/test/CodeGenCXX/x86_64-arguments.cpp @@ -115,3 +115,37 @@ namespace test6 { } // CHECK: define i32 @_ZN5test64testENS_5outerE(i64 %x.coerce0, i32 %x.coerce1) } + +namespace test7 { + struct StringRef {char* ptr; long len; }; + class A { public: ~A(); }; + A x(A, A, long, long, StringRef) { return A(); } + // Check that the StringRef is passed byval instead of expanded + // (which would split it between registers and memory). + // rdar://problem/9686430 + // CHECK: define void @_ZN5test71xENS_1AES0_llNS_9StringRefE({{.*}} byval align 8) + + // And a couple extra related tests: + A y(A, long double, long, long, StringRef) { return A(); } + // CHECK: define void @_ZN5test71yENS_1AEellNS_9StringRefE({{.*}} i8* + struct StringDouble {char * ptr; double d;}; + A z(A, A, A, A, A, StringDouble) { return A(); } + A zz(A, A, A, A, StringDouble) { return A(); } + // CHECK: define void @_ZN5test71zENS_1AES0_S0_S0_S0_NS_12StringDoubleE({{.*}} byval align 8) + // CHECK: define void @_ZN5test72zzENS_1AES0_S0_S0_NS_12StringDoubleE({{.*}} i8* +} + +namespace test8 { + // CHECK: declare void @_ZN5test83fooENS_1BE(%"class.test8::B"* byval align 8) + class A { + char big[17]; + }; + + class B : public A {}; + + void foo(B b); + void bar() { + B b; + foo(b); + } +} diff --git a/test/CodeGenObjC/arc-arm.m b/test/CodeGenObjC/arc-arm.m new file mode 100644 index 000000000000..a3d55c28f400 --- /dev/null +++ b/test/CodeGenObjC/arc-arm.m @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -triple armv7-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck %s + +id test0(void) { + extern id test0_helper(void); + // CHECK: [[T0:%.*]] = call arm_aapcscc i8* @test0_helper() + // CHECK-NEXT: ret i8* [[T0]] + return test0_helper(); +} + +void test1(void) { + extern id test1_helper(void); + // CHECK: [[T0:%.*]] = call arm_aapcscc i8* @test1_helper() + // CHECK-NEXT: call void asm sideeffect "mov\09r7, r7 + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], + // CHECK-NEXT: load + // CHECK-NEXT: call void @objc_release + // CHECK-NEXT: ret void + id x = test1_helper(); +} diff --git a/test/CodeGenObjC/arc-block-ivar-layout.m b/test/CodeGenObjC/arc-block-ivar-layout.m new file mode 100644 index 000000000000..50c35bacda06 --- /dev/null +++ b/test/CodeGenObjC/arc-block-ivar-layout.m @@ -0,0 +1,60 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-nonfragile-abi -fobjc-runtime-has-weak -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://8991729 + +__weak id wid; +void x(id y) {} +void y(int a) {} + +extern id opaque_id(); + +void f() { + __block int byref_int = 0; + char ch = 'a'; + char ch1 = 'b'; + char ch2 = 'c'; + short sh = 2; + const id bar = (id) opaque_id(); + id baz = 0; + __strong id strong_void_sta; + __block id byref_bab = (id)0; + __block id bl_var1; + int i; double dob; + +// The patterns here are a sequence of bytes, each saying first how +// many sizeof(void*) chunks to skip (high nibble) and then how many +// to scan (low nibble). A zero byte says that we've reached the end +// of the pattern. +// +// All of these patterns start with 01 3x because the block header on +// LP64 consists of an isa pointer (which we're supposed to scan for +// some reason) followed by three words (2 ints, a function pointer, +// and a descriptor pointer). + +// Test 1 +// byref int, short, char, char, char, id, id, strong id, byref id +// 01 35 10 00 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\015\10\00" + void (^b)() = ^{ + byref_int = sh + ch+ch1+ch2 ; + x(bar); + x(baz); + x((id)strong_void_sta); + x(byref_bab); + }; + b(); + +// Test 2 +// byref int, short, char, char, char, id, id, strong id, byref void*, byref id +// 01 36 10 00 +// CHECK-LP64: @"\01L_OBJC_CLASS_NAME_{{.*}}" = internal global [4 x i8] c"\016\10\00" + void (^c)() = ^{ + byref_int = sh + ch+ch1+ch2 ; + x(bar); + x(baz); + x((id)strong_void_sta); + x(wid); + bl_var1 = 0; + x(byref_bab); + }; +} diff --git a/test/CodeGenObjC/arc-bridged-cast.m b/test/CodeGenObjC/arc-bridged-cast.m new file mode 100644 index 000000000000..3354bda5bf00 --- /dev/null +++ b/test/CodeGenObjC/arc-bridged-cast.m @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +@end + +CFTypeRef CFCreateSomething(void); +CFStringRef CFCreateString(void); +CFTypeRef CFGetSomething(void); +CFStringRef CFGetString(void); + +id CreateSomething(void); +NSString *CreateNSString(void); + +// CHECK: define void @bridge_transfer_from_cf +void bridge_transfer_from_cf(int *i) { + // CHECK: store i32 7 + *i = 7; + // CHECK: call i8* @CFCreateSomething() + id obj1 = (__bridge_transfer id)CFCreateSomething(); + // CHECK-NOT: retain + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CFCreateSomething() + // CHECK-NOT: retain + // CHECK: store i32 13 + (void)(__bridge_transfer id)CFCreateSomething(), *i = 13; + // CHECK: call void @objc_release + // CHECK: store i32 17 + *i = 17; + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @bridge_from_cf +void bridge_from_cf(int *i) { + // CHECK: store i32 7 + *i = 7; + // CHECK: call i8* @CFCreateSomething() + id obj1 = (__bridge id)CFCreateSomething(); + // CHECK: objc_retainAutoreleasedReturnValue + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CFCreateSomething() + // CHECK-NOT: release + // CHECK: store i32 13 + (void)(__bridge id)CFCreateSomething(), *i = 13; + // CHECK: store i32 17 + *i = 17; + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @bridge_retained_of_cf +void bridge_retained_of_cf(int *i) { + *i = 7; + // CHECK: call i8* @CreateSomething() + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CreateSomething() + (__bridge_retained CFTypeRef)CreateSomething(), *i = 13; + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue + // CHECK: store i32 13 + // CHECK: store i32 17 + *i = 17; + // CHECK-NEXT: ret void +} + +// CHECK: define void @bridge_of_cf +void bridge_of_cf(int *i) { + // CHECK: store i32 7 + *i = 7; + // CHECK: call i8* @CreateSomething() + CFTypeRef cf1 = (__bridge CFTypeRef)CreateSomething(); + // CHECK-NOT: retain + // CHECK: store i32 11 + *i = 11; + // CHECK: call i8* @CreateSomething + (__bridge CFTypeRef)CreateSomething(), *i = 13; + // CHECK: store i32 13 + // CHECK-NOT: release + // CHECK: store i32 17 + *i = 17; + // CHECK-NEXT: ret void +} + diff --git a/test/CodeGenObjC/arc-compound-stmt.m b/test/CodeGenObjC/arc-compound-stmt.m new file mode 100644 index 000000000000..946d72f00591 --- /dev/null +++ b/test/CodeGenObjC/arc-compound-stmt.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fobjc-arc -o - %s +// rdar://9694706 + +typedef unsigned long NSUInteger; + +@interface NSString +- (NSString *)stringByAppendingString:(NSString *)aString; +- (NSString *)substringFromIndex:(NSUInteger)from; +@end + +@interface MyClass +- (void)inst; +@end + +@implementation MyClass + +- (void)inst; +{ + NSString *propName; + + NSString *capitalPropName = ({ + NSString *cap; + if (propName) + cap = [cap stringByAppendingString:[propName substringFromIndex:1]]; + cap; + }); +} + +@end diff --git a/test/CodeGenObjC/arc-foreach.m b/test/CodeGenObjC/arc-foreach.m new file mode 100644 index 000000000000..f9d7782fbedd --- /dev/null +++ b/test/CodeGenObjC/arc-foreach.m @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fblocks -fobjc-arc -fobjc-nonfragile-abi -fobjc-runtime-has-weak -triple x86_64-apple-darwin -O0 -emit-llvm %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://9503326 +// rdar://9606600 + +extern void use(id); +extern void use_block(void (^)(void)); +@class NSArray; + +void test0(NSArray *array) { + // 'x' should be initialized without a retain. + // We should actually do a non-constant capture, and that + // capture should require a retain. + for (id x in array) { + use_block(^{ use(x); }); + } +} + +// CHECK-LP64: define void @test0( +// CHECK-LP64: alloca [[ARRAY_T:%.*]]*, +// CHECK-LP64-NEXT: [[X:%.*]] = alloca i8*, +// CHECK-LP64-NEXT: [[STATE:%.*]] = alloca [[STATE_T:%.*]], +// CHECK-LP64-NEXT: alloca [16 x i8*], align 8 +// CHECK-LP64-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds [[STATE_T]]* [[STATE]], i32 0, i32 1 +// CHECK-LP64-NEXT: [[T1:%.*]] = load i8*** [[T0]] +// CHECK-LP64-NEXT: [[T2:%.*]] = getelementptr i8** [[T1]], i64 +// CHECK-LP64-NEXT: [[T3:%.*]] = load i8** [[T2]] +// CHECK-LP64-NEXT: store i8* [[T3]], i8** [[X]] + +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[X]] +// CHECK-LP64-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) +// CHECK-LP64-NEXT: store i8* [[T2]], i8** [[T0]] +// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] +// CHECK-LP64: call void @use_block( +// CHECK-LP64-NEXT: [[T1:%.*]] = load i8** [[T0]] +// CHECK-LP64-NEXT: call void @objc_release(i8* [[T1]]) + +// CHECK-LP64: define internal void @__test0_block_invoke +// CHECK-LP64: [[BLOCK:%.*]] = bitcast i8* {{%.*}} to [[BLOCK_T]]* +// CHECK-LP64-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64-NEXT: [[T2:%.*]] = load i8** [[T0]], align 8 +// CHECK-LP64-NEXT: call void @use(i8* [[T2]]) + +void test1(NSArray *array) { + for (__weak id x in array) { + use_block(^{ use(x); }); + } +} + +// CHECK-LP64: define void @test1( +// CHECK-LP64: alloca [[ARRAY_T:%.*]]*, +// CHECK-LP64-NEXT: [[X:%.*]] = alloca i8*, +// CHECK-LP64-NEXT: [[STATE:%.*]] = alloca [[STATE_T:%.*]], +// CHECK-LP64-NEXT: alloca [16 x i8*], align 8 +// CHECK-LP64-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds [[STATE_T]]* [[STATE]], i32 0, i32 1 +// CHECK-LP64-NEXT: [[T1:%.*]] = load i8*** [[T0]] +// CHECK-LP64-NEXT: [[T2:%.*]] = getelementptr i8** [[T1]], i64 +// CHECK-LP64-NEXT: [[T3:%.*]] = load i8** [[T2]] +// CHECK-LP64-NEXT: call i8* @objc_initWeak(i8** [[X]], i8* [[T3]]) + +// CHECK-LP64: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-LP64-NEXT: [[T1:%.*]] = call i8* @objc_loadWeak(i8** [[X]]) +// CHECK-LP64-NEXT: call i8* @objc_initWeak(i8** [[T0]], i8* [[T1]]) +// CHECK-LP64-NEXT: [[T1:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to +// CHECK-LP64: call void @use_block +// CHECK-LP64-NEXT: call void @objc_destroyWeak(i8** [[T0]]) +// CHECK-LP64-NEXT: call void @objc_destroyWeak(i8** [[X]]) diff --git a/test/CodeGenObjC/arc-ivar-layout.m b/test/CodeGenObjC/arc-ivar-layout.m new file mode 100644 index 000000000000..cb930fe3d365 --- /dev/null +++ b/test/CodeGenObjC/arc-ivar-layout.m @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -fobjc-runtime-has-weak -triple x86_64-apple-darwin -O0 -S %s -o %t-64.s +// RUN: FileCheck -check-prefix LP64 --input-file=%t-64.s %s +// rdar://8991729 + +@interface NSObject { + id isa; +} +@end + +@interface AllPointers : NSObject { + id foo; + id __strong bar; + NSObject *bletch; +} +@end + +@implementation AllPointers +@end +// CHECK-LP64: L_OBJC_CLASS_NAME_1: +// CHECK-LP64-NEXT: .asciz "\003" + +@class NSString, NSNumber; +@interface A : NSObject { + NSString *foo; + NSNumber *bar; + unsigned int bletch; + __weak id delegate; +} +@end + +@interface B : A { + unsigned int x; + NSString *y; + NSString *z; +} +@end + +@implementation A @end + +@implementation B @end + +// CHECK-LP64: L_OBJC_CLASS_NAME_15: +// CHECK-LP64-NEXT: .asciz "\022" + diff --git a/test/CodeGenObjC/arc-no-runtime.m b/test/CodeGenObjC/arc-no-runtime.m new file mode 100644 index 000000000000..39f7da3460e5 --- /dev/null +++ b/test/CodeGenObjC/arc-no-runtime.m @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-arc -fobjc-nonfragile-abi -emit-llvm %s -o - | FileCheck %s + +// rdar://problem/9224855 +void test0() { + id x = 0; + // CHECK: call void @objc_release( +} + +// CHECK: declare extern_weak void @objc_release( diff --git a/test/CodeGenObjC/arc-related-result-type.m b/test/CodeGenObjC/arc-related-result-type.m new file mode 100644 index 000000000000..c51757df5d8d --- /dev/null +++ b/test/CodeGenObjC/arc-related-result-type.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -o - %s | FileCheck %s + +@interface Test0 +- (id) self; +@end +void test0(Test0 *val) { + Test0 *x = [val self]; + +// CHECK: define void @test0( +// CHECK: [[VAL:%.*]] = alloca [[TEST0:%.*]]* +// CHECK-NEXT: [[X:%.*]] = alloca [[TEST0]]* +// CHECK-NEXT: bitcast +// CHECK-NEXT: call i8* @objc_retain( +// CHECK-NEXT: bitcast +// CHECK-NEXT: store +// CHECK-NEXT: load [[TEST0]]** [[VAL]], +// CHECK-NEXT: load +// CHECK-NEXT: bitcast +// CHECK-NEXT: [[T0:%.*]] = call i8* bitcast ( +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[TEST0]]* +// CHECK-NEXT: store [[TEST0]]* [[T2]], [[TEST0]]** [[X]] +// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[X]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST0]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) +// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[VAL]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST0]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) +// CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjC/arc-unbridged-cast.m b/test/CodeGenObjC/arc-unbridged-cast.m new file mode 100644 index 000000000000..0f3db09f1ffd --- /dev/null +++ b/test/CodeGenObjC/arc-unbridged-cast.m @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-nonfragile-abi -fobjc-arc -o - %s | FileCheck %s +// rdar://9744349 + +typedef const struct __CFString * CFStringRef; + +@interface I +@property CFStringRef P; +- (CFStringRef) CFMeth __attribute__((cf_returns_retained)); +- (CFStringRef) newSomething; +- (CFStringRef) P __attribute__((cf_returns_retained)); +@end + +@implementation I +@synthesize P; +- (id) Meth { + I* p1 = (id)[p1 P]; + id p2 = (id)[p1 CFMeth]; + id p3 = (id)[p1 newSomething]; + return (id) p1.P; +} +- (CFStringRef) CFMeth { return 0; } +- (CFStringRef) newSomething { return 0; } +- (CFStringRef) P { return 0; } +- (void) setP : (CFStringRef)arg {} +@end + +// rdar://9544832 +CFStringRef SomeOtherFunc() __attribute__((cf_returns_retained)); +id MMM() +{ + id obj = (id)((CFStringRef) __builtin___CFStringMakeConstantString ("" "Some CF String" "")); + if (obj) + return (id) SomeOtherFunc(); + return 0; +} + +// CHECK-NOT: call i8* @objc_retainAutoreleasedReturnValue diff --git a/test/CodeGenObjC/arc-unopt.m b/test/CodeGenObjC/arc-unopt.m new file mode 100644 index 000000000000..ed46052af848 --- /dev/null +++ b/test/CodeGenObjC/arc-unopt.m @@ -0,0 +1,69 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fblocks -fobjc-arc -o - %s | FileCheck %s + +// A test to ensure that we generate fused calls at -O0. + +@class Test0; +Test0 *test0(void) { + extern Test0 *test0_helper; + return test0_helper; + + // CHECK: [[LD:%.*]] = load [[TEST0:%.*]]** @test0_helper + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST0]]* [[LD]] to i8* + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleaseReturnValue(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[TEST0]]* + // CHECK-NEXT: ret [[TEST0]]* [[T2]] +} + +id test1(void) { + extern id test1_helper; + return test1_helper; + + // CHECK: [[LD:%.*]] = load i8** @test1_helper + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleaseReturnValue(i8* [[LD]]) + // CHECK-NEXT: ret i8* [[T0]] +} + +void test2(void) { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]]) + // CHECK-NEXT: ret void + __weak id x; +} + +id test3(void) { + extern id test3_helper(void); + // CHECK: [[T0:%.*]] = call i8* @test3_helper() + // CHECK-NEXT: ret i8* [[T0]] + return test3_helper(); +} + +@interface Test4 { id x; } @end +@interface Test4_sub : Test4 { id y; } @end +Test4 *test4(void) { + extern Test4_sub *test4_helper(void); + // CHECK: [[T0:%.*]] = call [[TEST4S:%.*]]* @test4_helper() + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST4S]]* [[T0]] to [[TEST4:%.*]]* + // CHECK-NEXT: ret [[TEST4]]* [[T1]] + return test4_helper(); +} + +// rdar://problem/9418404 +@class Test5; +void test5(void) { + Test5 *x, *y; + if ((x = y)) + y = 0; + +// CHECK: define void @test5() +// CHECK: [[X:%.*]] = alloca [[TEST5:%.*]]*, +// CHECK-NEXT: [[Y:%.*]] = alloca [[TEST5:%.*]]*, +// CHECK-NEXT: store [[TEST5]]* null, [[TEST5]]** [[X]], +// CHECK-NEXT: store [[TEST5]]* null, [[TEST5]]** [[Y]], +// CHECK-NEXT: [[T0:%.*]] = load [[TEST5]]** [[Y]], +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST5]]** [[X]] to i8** +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST5]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_storeStrong(i8** [[T1]], i8* [[T2]]) +// CHECK-NEXT: [[T3:%.*]] = icmp ne [[TEST5]]* [[T0]], null +// CHECK-NEXT: br i1 [[T3]], +} diff --git a/test/CodeGenObjC/arc-weak-property.m b/test/CodeGenObjC/arc-weak-property.m new file mode 100644 index 000000000000..c0796046446e --- /dev/null +++ b/test/CodeGenObjC/arc-weak-property.m @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fblocks -fobjc-arc -o - %s | FileCheck %s +// rdar://8899430 + +@interface WeakPropertyTest { + __weak id PROP; +} +@property () __weak id PROP; +@end + +@implementation WeakPropertyTest +@synthesize PROP; +@end + +// CHECK: define internal i8* @"\01-[WeakPropertyTest PROP]" +// CHECK: [[SELF:%.*]] = alloca [[WPT:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: store [[WPT]]* {{%.*}}, [[WPT]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = load [[WPT]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i64* @"OBJC_IVAR_$_WeakPropertyTest.PROP" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[WPT]]* [[T0]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[T1]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: [[T5:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T4]]) +// CHECK-NEXT: [[T6:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T5]]) +// CHECK-NEXT: ret i8* [[T6]] + +// CHECK: define internal void @"\01-[WeakPropertyTest setPROP:]" +// CHECK: [[SELF:%.*]] = alloca [[WPT:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: [[PROP:%.*]] = alloca i8*, +// CHECK-NEXT: store [[WPT]]* {{%.*}}, [[WPT]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[PROP]] +// CHECK-NEXT: [[V:%.*]] = load i8** [[PROP]] +// CHECK-NEXT: [[T0:%.*]] = load [[WPT]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i64* @"OBJC_IVAR_$_WeakPropertyTest.PROP" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[WPT]]* [[T0]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[T1]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: call i8* @objc_storeWeak(i8** [[T4]], i8* [[V]]) +// CHECK-NEXT: ret void + +// CHECK: define internal void @"\01-[WeakPropertyTest .cxx_destruct]" +// CHECK: [[SELF:%.*]] = alloca [[WPT:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: store [[WPT]]* {{%.*}}, [[WPT]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = load [[WPT]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i64* @"OBJC_IVAR_$_WeakPropertyTest.PROP" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[WPT]]* [[T0]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[T1]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT: call void @objc_destroyWeak(i8** [[T4]]) +// CHECK-NEXT: ret void diff --git a/test/CodeGenObjC/arc.m b/test/CodeGenObjC/arc.m new file mode 100644 index 000000000000..407b3eb77167 --- /dev/null +++ b/test/CodeGenObjC/arc.m @@ -0,0 +1,1554 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -fobjc-runtime-has-weak -O2 -disable-llvm-optzns -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck -check-prefix=CHECK-GLOBALS %s + +// CHECK: define void @test0 +void test0(id x) { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{.*}}) + // CHECK-NEXT: store i8* [[PARM]], i8** [[X]] + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) + // CHECK-NEXT: ret void +} + +// CHECK: define i8* @test1(i8* +id test1(id x) { + // CHECK: [[RET:%.*]] = alloca i8* + // CHECK-NEXT: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[Y:%.*]] = alloca i8* + // CHECK-NEXT: alloca i32 + // CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{%.*}}) + // CHECK-NEXT: store i8* [[PARM]], i8** [[X]] + // CHECK-NEXT: store i8* null, i8** [[Y]] + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], i8** [[RET]] + // CHECK-NEXT: store i32 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T0]]) + // CHECK-NEXT: ret i8* [[T1]] + id y; + return y; +} + +@interface Test2 ++ (void) class_method; +- (void) inst_method; +@end +@implementation Test2 + +// The self pointer of a class method is not retained. +// CHECK: define internal void @"\01+[Test2 class_method]" +// CHECK: alloca +// CHECK-NEXT: alloca +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK-NEXT: ret void ++ (void) class_method {} + +// The self pointer of an instance method is not retained. +// CHECK: define internal void @"\01-[Test2 inst_method]" +// CHECK: alloca +// CHECK-NEXT: alloca +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK-NEXT: ret void +- (void) inst_method {} +@end + +@interface Test3 ++ (id) alloc; +- (id) initWith: (int) x; +- (id) copy; +@end + +// CHECK: define void @test3_unelided() +void test3_unelided() { + extern void test3_helper(void); + + // CHECK: [[X:%.*]] = alloca [[TEST3:%.*]]* + // CHECK-NEXT: store [[TEST3]]* null, [[TEST3]]** [[X]], align + Test3 *x; + + // Call to +alloc. + // CHECK-NEXT: load {{.*}}* @"\01L_OBJC_CLASSLIST_REFERENCES_ + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[ALLOC:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @objc_release(i8* + [Test3 alloc]; + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST3]]** [[X]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST3]]* [[T0]] to i8* + // CHECK-NEXT: [[COPY:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend {{.*}})(i8* [[T1]], + // CHECK-NEXT: call void @objc_release(i8* [[COPY]]) nounwind + [x copy]; + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST3]]** [[X]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST3]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind + // CHECK-NEXT: ret void +} + +// CHECK: define void @test3() +void test3() { + // CHECK: [[X:%.*]] = alloca i8* + + id x = [[Test3 alloc] initWith: 5]; + + // Call to +alloc. + // CHECK-NEXT: load {{.*}}* @"\01L_OBJC_CLASSLIST_REFERENCES_ + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[ALLOC:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: bitcast + + // Call to -initWith: with elided retain of consumed argument. + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[INIT:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i32)*)(i8* + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[INIT:%.*]] = bitcast + // Assignment for initialization, retention elided. + // CHECK-NEXT: store i8* [[INIT]], i8** [[X]] + + // Call to -copy. + // CHECK-NEXT: [[V:%.*]] = load i8** [[X]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[COPY:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend {{.*}})(i8* [[V]], + + // Assignment to x. + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* [[COPY]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + + x = [x copy]; + + // Cleanup for x. + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + + // CHECK-NEXT: ret void +} + +// CHECK: define i8* @test4() +id test4() { + // Call to +alloc. + // CHECK: load {{.*}}* @"\01L_OBJC_CLASSLIST_REFERENCES_ + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[ALLOC:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: [[ALLOC:%.*]] = bitcast + + // Call to -initWith: with elided retain of consumed argument. + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: [[ALLOC:%.*]] = bitcast + // CHECK-NEXT: [[INIT:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i32)*)(i8* [[ALLOC]], + + // Initialization of return value, occuring within full-expression. + // Retain/release elided. + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[INIT:%.*]] = bitcast + // CHECK-NEXT: [[RET:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[INIT]]) + + // CHECK-NEXT: ret i8* [[RET]] + + return [[Test3 alloc] initWith: 6]; +} + +@interface Test5 { +@public + id var; +} +@end + +// CHECK: define void @test5 +void test5(Test5 *x, id y) { + // Prologue. + // CHECK: [[X:%.*]] = alloca [[TEST5:%.*]]*, + // CHECK-NEXT: [[Y:%.*]] = alloca i8* + // CHECK-NEXT: bitcast [[TEST5]]* {{%.*}} to i8* + // CHECK-NEXT: call i8* @objc_retain + // CHECK-NEXT: [[PARMX:%.*]] = bitcast i8* {{%.*}} to [[TEST5]]* + // CHECK-NEXT: store [[TEST5]]* [[PARMX]], [[TEST5]]** [[X]] + // CHECK-NEXT: call i8* @objc_retain + // CHECK-NEXT: store + + // CHECK-NEXT: load [[TEST5]]** [[X]] + // CHECK-NEXT: load i64* @"OBJC_IVAR_$_Test5.var" + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: [[VAR:%.*]] = bitcast + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[VAR]] + // CHECK-NEXT: store i8* null, i8** [[VAR]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + x->var = 0; + + // CHECK-NEXT: [[YVAL:%.*]] = load i8** [[Y]] + // CHECK-NEXT: load [[TEST5]]** [[X]] + // CHECK-NEXT: load i64* @"OBJC_IVAR_$_Test5.var" + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: [[VAR:%.*]] = bitcast + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* [[YVAL]]) nounwind + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[VAR]] + // CHECK-NEXT: store i8* [[T0]], i8** [[VAR]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + x->var = y; + + // Epilogue. + // CHECK-NEXT: [[TMP:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[TMP]]) nounwind + // CHECK-NEXT: [[T0:%.*]] = load [[TEST5]]** [[X]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST5]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind + // CHECK-NEXT: ret void +} + +id test6_helper(void) __attribute__((ns_returns_retained)); +// CHECK: define void @test6() +void test6() { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test6_helper() + // CHECK-NEXT: store i8* [[CALL]], i8** [[X]] + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: ret void + id x = test6_helper(); +} + +void test7_helper(id __attribute__((ns_consumed))); +// CHECK: define void @test7() +void test7() { + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) nounwind + // CHECK-NEXT: call void @test7_helper(i8* [[T1]]) + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: ret void + id x; + test7_helper(x); +} + +id test8_helper(void) __attribute__((ns_returns_retained)); +void test8() { + __unsafe_unretained id x = test8_helper(); + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[T0:%.*]] = call i8* @test8_helper() + // CHECK-NEXT: store i8* [[T0]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NOT: imprecise_release + // CHECK-NEXT: ret void +} + +id test9_helper(void) __attribute__((ns_returns_retained)); +void test9() { + id x __attribute__((objc_precise_lifetime)) = test9_helper(); + x = 0; + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test9_helper() + // CHECK-NEXT: store i8* [[CALL]], i8** [[X]] + + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind + // CHECK-NOT: clang.imprecise_release + + // CHECK-NEXT: ret void +} + +@interface Test10 +@property (retain) Test10 *me; +@end +void test10() { + Test10 *x; + id y = x.me.me; + + // CHECK: define void @test10() + // CHECK: [[X:%.*]] = alloca [[TEST10:%.*]]*, align + // CHECK-NEXT: [[Y:%.*]] = alloca i8*, align + // CHECK-NEXT: store [[TEST10]]* null, [[TEST10]]** [[X]] + // CHECK-NEXT: load [[TEST10]]** [[X]], align + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}" + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[T0:%.*]] = call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST10]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[V:%.*]] = bitcast i8* [[T2]] to [[TEST10]]* + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_{{[0-9]*}}" + // CHECK-NEXT: bitcast + // CHECK-NEXT: [[T0:%.*]] = call [[TEST10]]* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST10]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST10]]* + // CHECK-NEXT: [[T4:%.*]] = bitcast [[TEST10]]* [[T3]] to i8* + // CHECK-NEXT: store i8* [[T4]], i8** [[Y]] + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST10]]* [[V]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: [[T0:%.*]] = load [[TEST10]]** [[X]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST10]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: ret void +} + +void test11(id (*f)(void) __attribute__((ns_returns_retained))) { + // CHECK: define void @test11( + // CHECK: [[F:%.*]] = alloca i8* ()*, align + // CHECK-NEXT: [[X:%.*]] = alloca i8*, align + // CHECK-NEXT: store i8* ()* {{%.*}}, i8* ()** [[F]], align + // CHECK-NEXT: [[T0:%.*]] = load i8* ()** [[F]], align + // CHECK-NEXT: [[T1:%.*]] = call i8* [[T0]]() + // CHECK-NEXT: store i8* [[T1]], i8** [[X]], align + // CHECK-NEXT: [[T3:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T3]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: ret void + id x = f(); +} + +void test12(void) { + extern id test12_helper(void); + + // CHECK: define void @test12() + // CHECK: [[X:%.*]] = alloca i8*, align + // CHECK-NEXT: [[Y:%.*]] = alloca i8*, align + + __weak id x = test12_helper(); + // CHECK-NEXT: [[T0:%.*]] = call i8* @test12_helper() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[X]], i8* [[T1]]) + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + + x = test12_helper(); + // CHECK-NEXT: [[T0:%.*]] = call i8* @test12_helper() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[X]], i8* [[T1]]) + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + + id y = x; + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_loadWeakRetained(i8** [[X]]) + // CHECK-NEXT: store i8* [[T2]], i8** [[Y]], align + + // CHECK-NEXT: [[T4:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T4]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]]) + // CHECK-NEXT: ret void +} + +// Indirect consuming calls. +void test13(void) { + // CHECK: define void @test13() + // CHECK: [[X:%.*]] = alloca i8*, align + // CHECK-NEXT: store i8* null, i8** [[X]], align + id x; + + typedef void fnty(id __attribute__((ns_consumed))); + extern fnty *test13_func; + // CHECK-NEXT: [[FN:%.*]] = load void (i8*)** @test13_func, align + // CHECK-NEXT: [[X_VAL:%.*]] = load i8** [[X]], align + // CHECK-NEXT: [[X_TMP:%.*]] = call i8* @objc_retain(i8* [[X_VAL]]) nounwind + // CHECK-NEXT: call void [[FN]](i8* [[X_TMP]]) + test13_func(x); + + extern fnty ^test13_block; + // CHECK-NEXT: [[TMP:%.*]] = load void (i8*)** @test13_block, align + // CHECK-NEXT: [[BLOCK:%.*]] = bitcast void (i8*)* [[TMP]] to [[BLOCKTY:%.*]]* + // CHECK-NEXT: [[BLOCK_FN_PTR:%.*]] = getelementptr inbounds [[BLOCKTY]]* [[BLOCK]], i32 0, i32 3 + // CHECK-NEXT: [[BLOCK_OPAQUE:%.*]] = bitcast [[BLOCKTY]]* [[BLOCK]] to i8* + // CHECK-NEXT: [[X_VAL:%.*]] = load i8** [[X]], align + // CHECK-NEXT: [[X_TMP:%.*]] = call i8* @objc_retain(i8* [[X_VAL]]) nounwind + // CHECK-NEXT: [[BLOCK_FN_TMP:%.*]] = load i8** [[BLOCK_FN_PTR]] + // CHECK-NEXT: [[BLOCK_FN:%.*]] = bitcast i8* [[BLOCK_FN_TMP]] to void (i8*, i8*)* + // CHECK-NEXT: call void [[BLOCK_FN]](i8* [[BLOCK_OPAQUE]], i8* [[X_TMP]]) + test13_block(x); + + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NEXT: ret void +} + +@interface Test16_super @end +@interface Test16 : Test16_super { + id z; +} +@property (assign) int x; +@property (retain) id y; +- (void) dealloc; +@end +@implementation Test16 +@synthesize x; +@synthesize y; +- (void) dealloc { + // CHECK: define internal void @"\01-[Test16 dealloc]"( + // CHECK: [[SELF:%.*]] = alloca [[TEST16:%.*]]*, align + // CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align + // CHECK-NEXT: alloca + // CHECK-NEXT: store [[TEST16]]* {{%.*}}, [[TEST16]]** [[SELF]], align + // CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] + // CHECK-NEXT: [[BASE:%.*]] = load [[TEST16]]** [[SELF]] + + // Call super. + // CHECK-NEXT: [[BASE2:%.*]] = bitcast [[TEST16]]* [[BASE]] to i8* + // CHECK-NEXT: [[T0:%.*]] = getelementptr + // CHECK-NEXT: store i8* [[BASE2]], i8** [[T0]] + // CHECK-NEXT: load {{%.*}}** @"\01L_OBJC_CLASSLIST_SUP_REFS_$_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: call void bitcast (i8* ({{.*}})* @objc_msgSendSuper2 to void ( + // CHECK-NEXT: ret void +} + +// .cxx_destruct + // CHECK: define internal void @"\01-[Test16 .cxx_destruct]"( + // CHECK: [[SELF:%.*]] = alloca [[TEST16:%.*]]*, align + // CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align + // CHECK-NEXT: store [[TEST16]]* {{%.*}}, [[TEST16]]** [[SELF]], align + // CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] + // CHECK-NEXT: [[BASE:%.*]] = load [[TEST16]]** [[SELF]] + + // Destroy y. + // CHECK-NEXT: [[Y_OFF:%.*]] = load i64* @"OBJC_IVAR_$_Test16.y" + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST16]]* [[BASE]] to i8* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[Y_OFF]] + // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to i8** + // CHECK-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) nounwind + + // Destroy z. + // CHECK-NEXT: [[Z_OFF:%.*]] = load i64* @"OBJC_IVAR_$_Test16.z" + // CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST16]]* [[BASE]] to i8* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[Z_OFF]] + // CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to i8** + // CHECK-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) nounwind + + // CHECK-NEXT: ret void + +@end + +// This shouldn't crash. +@interface Test17A +@property (assign) int x; +@end +@interface Test17B : Test17A +@end +@implementation Test17B +- (int) x { return super.x + 1; } +@end + +// This shouldn't crash. +void test18(id (^maker)(void)) { + maker(); +} + +void test19() { + // CHECK: define void @test19() + // CHECK: [[X:%.*]] = alloca [5 x i8*], align 16 + // CHECK-NEXT: [[T0:%.*]] = bitcast [5 x i8*]* [[X]] to i8* + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 40, i32 16, i1 false) + id x[5]; + + extern id test19_helper(void); + x[2] = test19_helper(); + + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test19_helper() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) nounwind + // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [5 x i8*]* [[X]], i32 0, i64 2 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]] + // CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [5 x i8*]* [[X]], i32 0, i32 0 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 5 + // CHECK-NEXT: br label + + // CHECK: [[AFTER:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[NEXT:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds i8** [[AFTER]], i64 -1 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: [[EQ:%.*]] = icmp eq i8** [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[EQ]], + + // CHECK: ret void +} + +void test20(unsigned n) { + // CHECK: define void @test20 + // CHECK: [[N:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca i8* + // CHECK-NEXT: store i32 {{%.*}}, i32* [[N]], align 4 + + id x[n]; + + // Capture the VLA size. + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[DIM:%.*]] = zext i32 [[T0]] to i64 + + // Save the stack pointer. + // CHECK-NEXT: [[T0:%.*]] = call i8* @llvm.stacksave() + // CHECK-NEXT: store i8* [[T0]], i8** [[SAVED_STACK]] + + // Allocate the VLA. + // CHECK-NEXT: [[VLA:%.*]] = alloca i8*, i64 [[DIM]], align 16 + + // Zero-initialize. + // CHECK-NEXT: [[T0:%.*]] = bitcast i8** [[VLA]] to i8* + // CHECK-NEXT: [[T1:%.*]] = mul nuw i64 [[DIM]], 8 + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 [[T1]], i32 8, i1 false) + + // Destroy. + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[VLA]], i64 [[DIM]] + // CHECK-NEXT: [[EMPTY:%.*]] = icmp eq i8** [[VLA]], [[END]] + // CHECK-NEXT: br i1 [[EMPTY]] + + // CHECK: [[AFTER:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds i8** [[AFTER]], i64 -1 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: [[EQ:%.*]] = icmp eq i8** [[CUR]], [[VLA]] + // CHECK-NEXT: br i1 [[EQ]], + + // CHECK: [[T0:%.*]] = load i8** [[SAVED_STACK]] + // CHECK-NEXT: call void @llvm.stackrestore(i8* [[T0]]) + // CHECK-NEXT: ret void +} + +void test21(unsigned n) { + // CHECK: define void @test21 + // CHECK: [[N:%.*]] = alloca i32, align 4 + // CHECK-NEXT: [[SAVED_STACK:%.*]] = alloca i8* + // CHECK-NEXT: store i32 {{%.*}}, i32* [[N]], align 4 + + id x[2][n][3]; + + // Capture the VLA size. + // CHECK-NEXT: [[T0:%.*]] = load i32* [[N]], align 4 + // CHECK-NEXT: [[DIM:%.*]] = zext i32 [[T0]] to i64 + + // CHECK-NEXT: [[T0:%.*]] = call i8* @llvm.stacksave() + // CHECK-NEXT: store i8* [[T0]], i8** [[SAVED_STACK]] + + + // Allocate the VLA. + // CHECK-NEXT: [[T0:%.*]] = mul nuw i64 2, [[DIM]] + // CHECK-NEXT: [[VLA:%.*]] = alloca [3 x i8*], i64 [[T0]], align 16 + + // Zero-initialize. + // CHECK-NEXT: [[T0:%.*]] = bitcast [3 x i8*]* [[VLA]] to i8* + // CHECK-NEXT: [[T1:%.*]] = mul nuw i64 2, [[DIM]] + // CHECK-NEXT: [[T2:%.*]] = mul nuw i64 [[T1]], 24 + // CHECK-NEXT: call void @llvm.memset.p0i8.i64(i8* [[T0]], i8 0, i64 [[T2]], i32 8, i1 false) + + // Destroy. + // CHECK-NEXT: [[T0:%.*]] = mul nuw i64 2, [[DIM]] + // CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [3 x i8*]* [[VLA]], i32 0, i32 0 + // CHECK-NEXT: [[T1:%.*]] = mul nuw i64 [[T0]], 3 + // CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 [[T1]] + // CHECK-NEXT: [[EMPTY:%.*]] = icmp eq i8** [[BEGIN]], [[END]] + // CHECK-NEXT: br i1 [[EMPTY]] + + // CHECK: [[AFTER:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] + // CHECK-NEXT: [[CUR:%.*]] = getelementptr inbounds i8** [[AFTER]], i64 -1 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + // CHECK-NEXT: [[EQ:%.*]] = icmp eq i8** [[CUR]], [[BEGIN]] + // CHECK-NEXT: br i1 [[EQ]], + + // CHECK: [[T0:%.*]] = load i8** [[SAVED_STACK]] + // CHECK-NEXT: call void @llvm.stackrestore(i8* [[T0]]) + // CHECK-NEXT: ret void +} + +void test22(_Bool cond) { + id test22_helper(void) __attribute__((ns_returns_retained)); + + // CHECK: define void @test22( + // CHECK: [[COND:%.*]] = alloca i8, + // CHECK-NEXT: [[X:%.*]] = alloca i8*, + // CHECK-NEXT: [[RELCOND:%.*]] = alloca i1 + // CHECK-NEXT: [[RELVAL:%.*]] = alloca i8* + // CHECK-NEXT: store i1 false, i1* [[RELCOND]] + // CHECK-NEXT: zext + // CHECK-NEXT: store + // CHECK-NEXT: [[T0:%.*]] = load i8* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = trunc i8 [[T0]] to i1 + // CHECK-NEXT: br i1 [[T1]], + // CHECK: br label + // CHECK: [[CALL:%.*]] = call i8* @test22_helper() + // CHECK-NEXT: store i1 true, i1* [[RELCOND]] + // CHECK-NEXT: store i8* [[CALL]], i8** [[RELVAL]] + // CHECK-NEXT: br label + // CHECK: [[T0:%.*]] = phi i8* [ null, {{%.*}} ], [ [[CALL]], {{%.*}} ] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) nounwind + // CHECK-NEXT: store i8* [[T1]], i8** [[X]], + // CHECK-NEXT: [[REL:%.*]] = load i1* [[RELCOND]] + // CHECK-NEXT: br i1 [[REL]], + // CHECK: [[T0:%.*]] = load i8** [[RELVAL]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NEXT: br label + // CHECK: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind + // CHECK-NEXT: ret void + id x = (cond ? 0 : test22_helper()); +} + +// rdar://problem/8922540 +// Note that we no longer emit .release_ivars flags. +// CHECK-GLOBALS: @"\01l_OBJC_CLASS_RO_$_Test23" = internal global [[RO_T:%.*]] { i32 134, +@interface Test23 { id x; } @end +@implementation Test23 @end + +// CHECK-GLOBALS: @"\01l_OBJC_CLASS_RO_$_Test24" = internal global [[RO_T:%.*]] { i32 130, +@interface Test24 {} @end +@implementation Test24 @end + +int (^test25(int x))(void) { + // CHECK: define i32 ()* @test25( + // CHECK: [[X:%.*]] = alloca i32, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + // CHECK-NEXT: store i32 {{%.*}}, i32* [[X]] + // CHECK: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to i32 ()* + // CHECK-NEXT: [[T1:%.*]] = bitcast i32 ()* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainBlock(i8* [[T1]]) nounwind + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to i32 ()* + // CHECK-NEXT: [[T4:%.*]] = bitcast i32 ()* [[T3]] to i8* + // CHECK-NEXT: [[T5:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T4]]) nounwind + // CHECK-NEXT: [[T6:%.*]] = bitcast i8* [[T5]] to i32 ()* + // CHECK-NEXT: ret i32 ()* [[T6]] + return ^{ return x; }; +} + +// rdar://problem/8941012 +@interface Test26 { id x[4]; } @end +@implementation Test26 @end +// CHECK: define internal void @"\01-[Test26 .cxx_destruct]"( +// CHECK: [[SELF:%.*]] = load [[TEST26:%.*]]** +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test26.x" +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST26]]* [[SELF]] to i8* +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[OFFSET]] +// CHECK-NEXT: [[X:%.*]] = bitcast i8* [[T1]] to [4 x i8*]* +// CHECK-NEXT: [[BEGIN:%.*]] = getelementptr inbounds [4 x i8*]* [[X]], i32 0, i32 0 +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 4 +// CHECK-NEXT: br label +// CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] +// CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1 +// CHECK-NEXT: call void @objc_storeStrong(i8** [[CUR]], i8* null) +// CHECK-NEXT: [[ISDONE:%.*]] = icmp eq i8** [[CUR]], [[BEGIN]] +// CHECK-NEXT: br i1 [[ISDONE]], +// CHECK: ret void + +// Check that 'init' retains self. +@interface Test27 +- (id) init; +@end +@implementation Test27 +- (id) init { return self; } +// CHECK: define internal i8* @"\01-[Test27 init]" +// CHECK: [[RET:%.*]] = alloca i8*, +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST27:%.*]]*, +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, +// CHECK-NEXT: [[DEST:%.*]] = alloca i32 +// CHECK-NEXT: store [[TEST27]]* {{%.*}}, [[TEST27]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] +// CHECK-NEXT: [[T2:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T2]], i8** [[RET]] +// CHECK-NEXT: store i32 {{[0-9]+}}, i32* [[DEST]] +// CHECK-NEXT: [[T0:%.*]] = load [[TEST27]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST27]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + +@end + +// rdar://problem/8087194 +@interface Test28 +@property (copy) id prop; +@end +@implementation Test28 +@synthesize prop; +@end +// CHECK: define internal void @"\01-[Test28 .cxx_destruct]" +// CHECK: [[SELF:%.*]] = load [[TEST28:%.*]]** +// CHECK-NEXT: [[OFFSET:%.*]] = load i64* @"OBJC_IVAR_$_Test28.prop" +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST28]]* [[SELF]] to i8* +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds i8* [[T0]], i64 [[OFFSET]] +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to i8** +// CHECK-NEXT: call void @objc_storeStrong(i8** [[T2]], i8* null) +// CHECK-NEXT: ret void + +@interface Test29_super +- (id) initWithAllocator: (id) allocator; +@end +@interface Test29 : Test29_super +- (id) init; +- (id) initWithAllocator: (id) allocator; +@end +@implementation Test29 +static id _test29_allocator = 0; +- (id) init { +// CHECK: define internal i8* @"\01-[Test29 init]"([[TEST29:%.*]]* {{%.*}}, +// CHECK: [[RET:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST29]]*, align 8 +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[CLEANUP:%.*]] = alloca i32 +// CHECK-NEXT: store [[TEST29]]* {{%.*}}, [[TEST29]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] + +// Evaluate arguments. Note that the send argument is evaluated +// before the zeroing of self. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]], align 8 +// CHECK-NEXT: [[T1:%.*]] = load i8** @_test29_allocator, align 8 + +// Implicit null of 'self', i.e. direct transfer of ownership. +// CHECK-NEXT: store [[TEST29]]* null, [[TEST29]]** [[SELF]] + +// Actual message send. +// CHECK-NEXT: [[T2:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[T3:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to i8* (i8*, i8*, i8*)*)(i8* [[T3]], i8* [[T2]], i8* [[T1]]) + +// Implicit write of result back into 'self'. This is not supposed to +// be detectable because we're supposed to ban accesses to the old +// self value past the delegate init call. +// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[CALL]] to [[TEST29]]* +// CHECK-NEXT: store [[TEST29]]* [[T0]], [[TEST29]]** [[SELF]] + +// Return statement. +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[CALL]] +// CHECK-NEXT: [[CALL:%.*]] = bitcast +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[CALL]]) nounwind +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] +// CHECK-NEXT: [[T1:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T1]], i8** [[RET]] +// CHECK-NEXT: store i32 1, i32* [[CLEANUP]] + +// Cleanup. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + +// Return. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + return [self initWithAllocator: _test29_allocator]; +} +- (id) initWithAllocator: (id) allocator { +// CHECK: define internal i8* @"\01-[Test29 initWithAllocator:]"( +// CHECK: [[RET:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST29]]*, align 8 +// CHECK-NEXT: [[CMD:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: [[ALLOCATOR:%.*]] = alloca i8*, align 8 +// CHECK-NEXT: alloca +// CHECK-NEXT: [[CLEANUP:%.*]] = alloca i32 +// CHECK-NEXT: store [[TEST29]]* {{%.*}}, [[TEST29]]** [[SELF]] +// CHECK-NEXT: store i8* {{%.*}}, i8** [[CMD]] +// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retain(i8* {{%.*}}) +// CHECK-NEXT: store i8* [[T0]], i8** [[ALLOCATOR]] + +// Evaluate arguments. Note that the send argument is evaluated +// before the zeroing of self. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i8** [[ALLOCATOR]], align 8 + +// Implicit null of 'self', i.e. direct transfer of ownership. +// CHECK-NEXT: store [[TEST29]]* null, [[TEST29]]** [[SELF]] + +// Actual message send. +// CHECK: [[CALL:%.*]] = call {{.*}} @objc_msgSendSuper2 + +// Implicit write of result back into 'self'. This is not supposed to +// be detectable because we're supposed to ban accesses to the old +// self value past the delegate init call. +// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[CALL]] to [[TEST29]]* +// CHECK-NEXT: store [[TEST29]]* [[T0]], [[TEST29]]** [[SELF]] + +// Assignment. +// CHECK-NEXT: [[T0:%.*]] = bitcast i8* [[CALL]] to [[TEST29]]* +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[TEST29]]* +// CHECK-NEXT: [[T4:%.*]] = load [[TEST29]]** [[SELF]], align +// CHECK-NEXT: store [[TEST29]]* [[T3]], [[TEST29]]** [[SELF]], align +// CHECK-NEXT: [[T5:%.*]] = bitcast [[TEST29]]* [[T4]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T5]]) + +// Return statement. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) nounwind +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] +// CHECK-NEXT: [[T2:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T2]], i8** [[RET]] +// CHECK-NEXT: store i32 1, i32* [[CLEANUP]] + +// Cleanup. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[ALLOCATOR]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release + +// CHECK-NEXT: [[T0:%.*]] = load [[TEST29]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST29]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) nounwind, !clang.imprecise_release + +// Return. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + self = [super initWithAllocator: allocator]; + return self; +} +@end + +typedef struct Test30_helper Test30_helper; +@interface Test30 +- (id) init; +- (Test30_helper*) initHelper; +@end +@implementation Test30 { +char *helper; +} +- (id) init { +// CHECK: define internal i8* @"\01-[Test30 init]"([[TEST30:%.*]]* {{%.*}}, +// CHECK: [[RET:%.*]] = alloca i8* +// CHECK-NEXT: [[SELF:%.*]] = alloca [[TEST30]]* +// CHECK-NEXT: alloca i8* +// CHECK-NEXT: alloca i32 +// CHECK-NEXT: store [[TEST30]]* {{%.*}}, [[TEST30]]** [[SELF]] +// CHECK-NEXT: store + +// Call. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* +// CHECK-NEXT: [[CALL:%.*]] = call [[TEST30_HELPER:%.*]]* bitcast {{.*}} @objc_msgSend {{.*}}(i8* [[T2]], i8* [[T1]]) + +// Assignment. +// CHECK-NEXT: [[T0:%.*]] = bitcast [[TEST30_HELPER]]* [[CALL]] to i8* +// CHECK-NEXT: [[T1:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[IVAR:%.*]] = load i64* @"OBJC_IVAR_$_Test30.helper" +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST30]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds i8* [[T2]], i64 [[IVAR]] +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to i8** +// CHECK-NEXT#: [[T5:%.*]] = load i8** [[T4]] +// CHECK-NEXT#: [[T6:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT#: call void @objc_release(i8* [[T5]]) +// CHECK-NEXT: store i8* [[T0]], i8** [[T4]] + +// Return. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) +// CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] +// CHECK-NEXT: [[T2:%.*]] = bitcast +// CHECK-NEXT: store i8* [[T2]], i8** [[RET]] +// CHECK-NEXT: store i32 1 + +// Cleanup. +// CHECK-NEXT: [[T0:%.*]] = load [[TEST30]]** [[SELF]] +// CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST30]]* [[T0]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T1]]) + +// Epilogue. +// CHECK-NEXT: [[T0:%.*]] = load i8** [[RET]] +// CHECK-NEXT: ret i8* [[T0]] + self->helper = [self initHelper]; + return self; +} +- (Test30_helper*) initHelper { +// CHECK: define internal [[TEST30_HELPER]]* @"\01-[Test30 initHelper]"( +// CHECK: alloca +// CHECK-NEXT: alloca +// CHECK-NEXT: store +// CHECK-NEXT: store +// CHECK-NEXT: ret [[TEST30_HELPER]]* null + return 0; +} + +@end + +void test31(id x) { +// CHECK: define void @test31( +// CHECK: [[X:%.*]] = alloca i8*, +// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], +// CHECK-NEXT: [[PARM:%.*]] = call i8* @objc_retain(i8* {{%.*}}) +// CHECK-NEXT: store i8* [[PARM]], i8** [[X]] +// CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]], +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]], +// CHECK-NEXT: bitcast +// CHECK-NEXT: call void @test31_helper( +// CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release +// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) nounwind, !clang.imprecise_release +// CHECK-NEXT: ret void + extern void test31_helper(id (^)(void)); + test31_helper(^{ return x; }); +} + +__attribute__((ns_returns_retained)) id test32(void) { +// CHECK: define i8* @test32() +// CHECK: [[CALL:%.*]] = call i8* @test32_helper() +// CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) +// CHECK-NEXT: ret i8* [[T0]] + extern id test32_helper(void); + return test32_helper(); +} + +@class Test33_a; +@interface Test33 +- (void) give: (Test33_a **) x; +- (void) take: (Test33_a **) x; +- (void) giveStrong: (out __strong Test33_a **) x; +- (void) takeStrong: (inout __strong Test33_a **) x; +- (void) giveOut: (out Test33_a **) x; +@end +void test33(Test33 *ptr) { + Test33_a *a; + [ptr give: &a]; + [ptr take: &a]; + [ptr giveStrong: &a]; + [ptr takeStrong: &a]; + [ptr giveOut: &a]; + + // CHECK: define void @test33([[TEST33:%.*]]* + // CHECK: [[PTR:%.*]] = alloca [[TEST33]]* + // CHECK-NEXT: [[A:%.*]] = alloca [[A_T:%.*]]* + // CHECK-NEXT: [[TEMP0:%.*]] = alloca [[A_T]]* + // CHECK-NEXT: [[TEMP1:%.*]] = alloca [[A_T]]* + // CHECK-NEXT: [[TEMP2:%.*]] = alloca [[A_T]]* + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_retain + // CHECK-NEXT: bitcast + // CHECK-NEXT: store + // CHECK-NEXT: store [[A_T]]* null, [[A_T]]** [[A]] + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T0]], [[A_T]]** [[TEMP0]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[TEMP0]]) + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[TEMP0]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A_T]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A_T]]* + // CHECK-NEXT: [[T4:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T3]], [[A_T]]** [[A]] + // CHECK-NEXT: [[T5:%.*]] = bitcast [[A_T]]* [[T4]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T5]]) + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T0]], [[A_T]]** [[TEMP1]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[TEMP1]]) + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A_T]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A_T]]* + // CHECK-NEXT: [[T4:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T3]], [[A_T]]** [[A]] + // CHECK-NEXT: [[T5:%.*]] = bitcast [[A_T]]* [[T4]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T5]]) + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[A]]) + + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[A]]) + + // 'out' + // CHECK-NEXT: load [[TEST33]]** [[PTR]] + // CHECK-NEXT: store [[A_T]]* null, [[A_T]]** [[TEMP2]] + // CHECK-NEXT: load i8** @"\01L_OBJC_SELECTOR_REFERENCES_ + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_msgSend{{.*}}, [[A_T]]** [[TEMP2]]) + // CHECK-NEXT: [[T0:%.*]] = load [[A_T]]** [[TEMP2]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[A_T]]* [[T0]] to i8* + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = bitcast i8* [[T2]] to [[A_T]]* + // CHECK-NEXT: [[T4:%.*]] = load [[A_T]]** [[A]] + // CHECK-NEXT: store [[A_T]]* [[T3]], [[A_T]]** [[A]] + // CHECK-NEXT: [[T5:%.*]] = bitcast [[A_T]]* [[T4]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T5]]) + + // CHECK-NEXT: load + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_release + // CHECK-NEXT: load + // CHECK-NEXT: bitcast + // CHECK-NEXT: objc_release + // CHECK-NEXT: ret void +} + +void test34(int cond) { + __strong id strong; + __weak id weak; + extern void test34_sink(id *); + test34_sink(cond ? &strong : 0); + test34_sink(cond ? &weak : 0); + + // CHECK: define void @test34( + // CHECK: [[COND:%.*]] = alloca i32 + // CHECK-NEXT: [[STRONG:%.*]] = alloca i8* + // CHECK-NEXT: [[WEAK:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP1:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP2:%.*]] = alloca i8* + // CHECK-NEXT: store i32 + // CHECK-NEXT: store i8* null, i8** [[STRONG]] + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[WEAK]], i8* null) + + // CHECK-NEXT: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP1]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP1]] + // CHECK-NEXT: br label + // CHECK: call void @test34_sink(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T1]], i8** [[ARG]] + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + // CHECK-NEXT: br label + + // CHECK: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP2]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[ARG]]) + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP2]] + // CHECK-NEXT: br label + // CHECK: call void @test34_sink(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP2]] + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[ARG]], i8* [[T0]]) + // CHECK-NEXT: br label + + // CHECK: call void @objc_destroyWeak(i8** [[WEAK]]) + // CHECK: ret void +} + +void test35(void (^sink)(id*)) { + __strong id strong; + sink(&strong); + + // CHECK: define void @test35( + // CHECK: [[SINK:%.*]] = alloca void (i8**)* + // CHECK-NEXT: [[STRONG:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP:%.*]] = alloca i8* + // CHECK-NEXT: bitcast void (i8**)* {{%.*}} to i8* + // CHECK-NEXT: call i8* @objc_retain( + // CHECK-NEXT: bitcast i8* + // CHECK-NEXT: store void (i8**)* {{%.*}}, void (i8**)** [[SINK]] + // CHECK-NEXT: store i8* null, i8** [[STRONG]] + + // CHECK-NEXT: load void (i8**)** [[SINK]] + // CHECK-NEXT: bitcast + // CHECK-NEXT: getelementptr + // CHECK-NEXT: [[BLOCK:%.*]] = bitcast + // CHECK-NEXT: [[T0:%.*]] = load i8** [[STRONG]] + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP1]] + // CHECK-NEXT: [[F0:%.*]] = load i8** + // CHECK-NEXT: [[F1:%.*]] = bitcast i8* [[F0]] to void (i8*, i8**)* + // CHECK-NEXT: call void [[F1]](i8* [[BLOCK]], i8** [[TEMP1]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8** [[STRONG]] + // CHECK-NEXT: store i8* [[T1]], i8** [[STRONG]] + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + + // CHECK-NEXT: [[T0:%.*]] = load i8** [[STRONG]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + + // CHECK-NEXT: load void (i8**)** [[SINK]] + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @objc_release + // CHECK-NEXT: ret void + +} + +// CHECK: define void @test36 +void test36(id x) { + // CHECK: [[X:%.*]] = alloca i8* + + // CHECK: call i8* @objc_retain + // CHECK: call i8* @objc_retain + // CHECK: call i8* @objc_retain + id array[3] = { @"A", x, @"y" }; + + // CHECK: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + x = 0; + + // CHECK: br label + // CHECK: call void @objc_release + // CHECK: br i1 + + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +@class Test37; +void test37(void) { + extern void test37_helper(id *); + Test37 *var; + test37_helper(&var); + + // CHECK: define void @test37() + // CHECK: [[VAR:%.*]] = alloca [[TEST37:%.*]]*, + // CHECK-NEXT: [[TEMP:%.*]] = alloca i8* + // CHECK-NEXT: store [[TEST37]]* null, [[TEST37]]** [[VAR]] + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST37]]** [[VAR]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST37]]* [[T0]] to i8* + // CHECK-NEXT: store i8* [[T1]], i8** [[TEMP]] + // CHECK-NEXT: call void @test37_helper(i8** [[TEMP]]) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[TEMP]] + // CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to [[TEST37]]* + // CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST37]]* [[T1]] to i8* + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[TEST37]]* + // CHECK-NEXT: [[T5:%.*]] = load [[TEST37]]** [[VAR]] + // CHECK-NEXT: store [[TEST37]]* [[T4]], [[TEST37]]** [[VAR]] + // CHECK-NEXT: [[T6:%.*]] = bitcast [[TEST37]]* [[T5]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T6]]) + + // CHECK-NEXT: [[T0:%.*]] = load [[TEST37]]** [[VAR]] + // CHECK-NEXT: [[T1:%.*]] = bitcast [[TEST37]]* [[T0]] to i8* + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: ret void +} + +void test38(void) { + id test38_source(void); + void test38_helper(void (^)(void)); + __block id var = test38_source(); + test38_helper(^{ var = 0; }); + + // CHECK: define void @test38() + // CHECK: [[VAR:%.*]] = alloca [[BYREF_T:%.*]], + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 2 + // 0x02000000 - has copy/dispose helpers + // CHECK-NEXT: store i32 33554432, i32* [[T0]] + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // CHECK-NEXT: [[T0:%.*]] = call i8* @test38_source() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], i8** [[SLOT]] + // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // 0x42000000 - has signature, copy/dispose helpers + // CHECK: store i32 1107296256, + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: store i8* [[T0]], i8** + // CHECK: call void @test38_helper( + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) + // CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__Block_byref_object_copy_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: load i8** + // CHECK-NEXT: bitcast i8* {{%.*}} to [[BYREF_T]]* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: [[T2:%.*]] = load i8** [[T1]] + // CHECK-NEXT: store i8* [[T2]], i8** [[T0]] + // CHECK-NEXT: store i8* null, i8** [[T1]] + + // CHECK: define internal void @__Block_byref_object_dispose_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + + // CHECK: define internal void @__test38_block_invoke_ + // CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[SLOT]], align 8 + // CHECK-NEXT: store i8* null, i8** [[SLOT]], + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__copy_helper_block_ + // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) + + // CHECK: define internal void @__destroy_helper_block_ + // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) +} + +void test39(void) { + extern id test39_source(void); + void test39_helper(void (^)(void)); + __unsafe_unretained id var = test39_source(); + test39_helper(^{ (void) var; }); + + // CHECK: define void @test39() + // CHECK: [[VAR:%.*]] = alloca i8* + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + // CHECK: [[T0:%.*]] = call i8* @test39_source() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], i8** [[VAR]], + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // 0x40000000 - has signature but no copy/dispose + // CHECK: store i32 1073741824, i32* + // CHECK: [[CAPTURE:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[VAR]] + // CHECK-NEXT: store i8* [[T0]], i8** [[CAPTURE]] + // CHECK-NEXT: [[T0:%.*]] = bitcast [[BLOCK_T]]* [[BLOCK]] to + // CHECK: call void @test39_helper + // CHECK-NEXT: ret void +} + +void test40(void) { + id test40_source(void); + void test40_helper(void (^)(void)); + __block __weak id var = test40_source(); + test40_helper(^{ var = 0; }); + + // CHECK: define void @test40() + // CHECK: [[VAR:%.*]] = alloca [[BYREF_T:%.*]], + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 2 + // 0x02000000 - has copy/dispose helpers + // CHECK-NEXT: store i32 33554432, i32* [[T0]] + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // CHECK-NEXT: [[T0:%.*]] = call i8* @test40_source() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T1]]) + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: [[SLOT:%.*]] = getelementptr inbounds [[BYREF_T]]* [[VAR]], i32 0, i32 6 + // 0x42000000 - has signature, copy/dispose helpers + // CHECK: store i32 1107296256, + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: store i8* [[T0]], i8** + // CHECK: call void @test40_helper( + // CHECK: [[T0:%.*]] = bitcast [[BYREF_T]]* [[VAR]] to i8* + // CHECK-NEXT: call void @_Block_object_dispose(i8* [[T0]], i32 8) + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[SLOT]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__Block_byref_object_copy_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: load i8** + // CHECK-NEXT: bitcast i8* {{%.*}} to [[BYREF_T]]* + // CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: call void @objc_moveWeak(i8** [[T0]], i8** [[T1]]) + + // CHECK: define internal void @__Block_byref_object_dispose_ + // CHECK: [[T0:%.*]] = getelementptr inbounds [[BYREF_T]]* {{%.*}}, i32 0, i32 6 + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[T0]]) + + // CHECK: define internal void @__test40_block_invoke_ + // CHECK: [[SLOT:%.*]] = getelementptr inbounds {{.*}}, i32 0, i32 6 + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[SLOT]], i8* null) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__copy_helper_block_ + // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) + // CHECK: call void @_Block_object_assign(i8* {{%.*}}, i8* {{%.*}}, i32 8) + + // CHECK: define internal void @__destroy_helper_block_ + // 0x8 - FIELD_IS_BYREF (no FIELD_IS_WEAK because clang in control) + // CHECK: call void @_Block_object_dispose(i8* {{%.*}}, i32 8) +} + +void test41(void) { + id test41_source(void); + void test41_helper(void (^)(void)); + void test41_consume(id); + __weak id var = test41_source(); + test41_helper(^{ test41_consume(var); }); + + // CHECK: define void @test41() + // CHECK: [[VAR:%.*]] = alloca i8*, + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], + // CHECK: [[T0:%.*]] = call i8* @test41_source() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[VAR]], i8* [[T1]]) + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // 0x42000000 - has signature, copy/dispose helpers + // CHECK: store i32 1107296256, + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[VAR]]) + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[SLOT]], i8* [[T0]]) + // CHECK: call void @test41_helper( + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[SLOT]]) + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[VAR]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__test41_block_invoke_ + // CHECK: [[SLOT:%.*]] = getelementptr inbounds [[BLOCK_T]]* {{%.*}}, i32 0, i32 5 + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[SLOT]]) + // CHECK-NEXT: call void @test41_consume(i8* [[T0]]) + // CHECK-NEXT: ret void + + // CHECK: define internal void @__copy_helper_block_ + // CHECK: getelementptr + // CHECK-NEXT: getelementptr + // CHECK-NEXT: call void @objc_copyWeak( + + // CHECK: define internal void @__destroy_helper_block_ + // CHECK: getelementptr + // CHECK-NEXT: call void @objc_destroyWeak( +} + +@interface Test42 @end +@implementation Test42 +- (void) test { +// CHECK: define internal void @"\01-[Test42 test]" +// CHECK: [[SELF:%.*]] = alloca [[TEST42:%.*]]*, +// CHECK-NEXT: alloca i8* +// CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], +// CHECK: store +// CHECK-NEXT: store +// CHECK: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 +// CHECK-NEXT: [[T1:%.*]] = load [[TEST42]]** [[SELF]], +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST42]]* [[T1]] to i8* +// CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retain(i8* [[T2]]) +// CHECK-NEXT: [[T4:%.*]] = bitcast i8* [[T3]] to [[TEST42]]* +// CHECK-NEXT: store [[TEST42]]* [[T4]], [[TEST42]]** [[T0]] +// CHECK-NEXT: bitcast [[BLOCK_T]]* [[BLOCK]] to +// CHECK: call void @test42_helper( +// CHECK-NEXT: [[T1:%.*]] = load [[TEST42]]** [[T0]] +// CHECK-NEXT: [[T2:%.*]] = bitcast [[TEST42]]* [[T1]] to i8* +// CHECK-NEXT: call void @objc_release(i8* [[T2]]) +// CHECK-NEXT: ret void + + extern void test42_helper(void (^)(void)); + test42_helper(^{ (void) self; }); +} +@end + +@interface Test43 @end +@implementation Test43 +- (id) test __attribute__((ns_returns_retained)) { + extern id test43_produce(void); + return test43_produce(); + // CHECK: call i8* @test43_produce() + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue( + // CHECK-NEXT: ret +} +@end + +id test44(void) { + typedef id __attribute__((ns_returns_retained)) blocktype(void); + extern test44_consume_block(blocktype^); + return ^blocktype { + extern id test44_produce(void); + return test44_produce(); + }(); + +// CHECK: define i8* @test44( +// CHECK: load i8** getelementptr +// CHECK-NEXT: bitcast i8* +// CHECK-NEXT: call i8* +// CHECK-NEXT: call i8* @objc_autoreleaseReturnValue +// CHECK-NEXT: ret i8* + +// CHECK: call i8* @test44_produce() +// CHECK-NEXT: call i8* @objc_retain +// CHECK-NEXT: ret i8* +} + +@interface Test45 +@property (retain) id x; +@end +@implementation Test45 +@synthesize x; +@end +// CHECK: define internal i8* @"\01-[Test45 x]"( +// CHECK: [[CALL:%.*]] = call i8* @objc_getProperty( +// CHECK-NEXT: ret i8* [[CALL]] + +// rdar://problem/9315552 +void test46(__weak id *wp, __weak volatile id *wvp) { + extern id test46_helper(void); + + // TODO: this is sub-optimal, we should retain at the actual call site. + + // CHECK: [[T0:%.*]] = call i8* @test46_helper() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_retain(i8* [[T3]]) + // CHECK-NEXT: store i8* [[T4]], i8** + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + id x = *wp = test46_helper(); + + // CHECK: [[T0:%.*]] = call i8* @test46_helper() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_retain(i8* [[T3]]) + // CHECK-NEXT: store i8* [[T4]], i8** + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + id y = *wvp = test46_helper(); +} + +// rdar://problem/9378887 +void test47(void) { + extern id test47_helper(void); + id x = x = test47_helper(); + + // CHECK: define void @test47() + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test47_helper() + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) + // CHECK-NEXT: [[T1:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* [[T0]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T3:%.*]] = load i8** [[X]] + // CHECK-NEXT: store i8* [[T2]], i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T3]]) + // CHECK-NEXT: [[T4:%.*]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[T4]]) + // CHECK-NEXT: ret void +} + +void test48(void) { + extern id test48_helper(void); + __weak id x = x = test48_helper(); + // CHECK: define void @test48() + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_initWeak(i8** [[X]], i8* null) + // CHECK-NEXT: [[T1:%.*]] = call i8* @test48_helper() + // CHECK-NEXT: [[T2:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T1]]) + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T2]]) + // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_storeWeak(i8** [[X]], i8* [[T3]]) + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[X]]) + // CHECK-NEXT: ret void +} + +void test49(void) { + extern id test49_helper(void); + __autoreleasing id x = x = test49_helper(); + // CHECK: define void @test49() + // CHECK: [[X:%.*]] = alloca i8* + // CHECK-NEXT: store i8* null, i8** [[X]] + // CHECK-NEXT: [[CALL:%.*]] = call i8* @test49_helper() + // CHECK-NEXT: [[T0:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[CALL]]) + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_autorelease(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T2]], i8** [[X]] + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_retainAutorelease(i8* [[T1]]) + // CHECK-NEXT: store i8* [[T3]], i8** [[X]] + // CHECK-NEXT: ret void +} + +// rdar://9380136 +id x(); +void test50(id y) { + ({x();}); +// CHECK: [[T0:%.*]] = call i8* @objc_retain +// CHECK: call void @objc_release +} + + +// rdar://9400762 +struct CGPoint { + float x; + float y; +}; +typedef struct CGPoint CGPoint; + +@interface Foo +@property (assign) CGPoint point; +@end + +@implementation Foo +@synthesize point; +@end + +// rdar://problem/9400398 +id test52(void) { + id test52_helper(int) __attribute__((ns_returns_retained)); + return ({ int x = 5; test52_helper(x); }); + +// CHECK: define i8* @test52() +// CHECK: [[X:%.*]] = alloca i32 +// CHECK-NEXT: store i32 5, i32* [[X]], +// CHECK-NEXT: [[T0:%.*]] = load i32* [[X]], +// CHECK-NEXT: [[T1:%.*]] = call i8* @test52_helper(i32 [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = call i8* @objc_autoreleaseReturnValue(i8* [[T1]]) +// CHECK-NEXT: ret i8* [[T2]] +} + +// rdar://problem/9400644 +void test53(void) { + id test53_helper(void); + id x = ({ id y = test53_helper(); y; }); + (void) x; +// CHECK: define void @test53() +// CHECK: [[X:%.*]] = alloca i8*, +// CHECK-NEXT: [[Y:%.*]] = alloca i8*, +// CHECK-NEXT: [[T0:%.*]] = call i8* @test53_helper() +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) +// CHECK-NEXT: store i8* [[T1]], i8** [[Y]], +// CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]], +// CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) +// CHECK-NEXT: [[T2:%.*]] = load i8** [[Y]] +// CHECK-NEXT: call void @objc_release(i8* [[T2]]) +// CHECK-NEXT: store i8* [[T1]], i8** [[X]], +// CHECK-NEXT: load i8** [[X]], +// CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-NEXT: ret void +} + +// <rdar://problem/9758798> +// CHECK: define void @test54(i32 %first, ...) +void test54(int first, ...) { + __builtin_va_list arglist; + // CHECK: call void @llvm.va_start + __builtin_va_start(arglist, first); + // CHECK: call i8* @objc_retain + id obj = __builtin_va_arg(arglist, id); + // CHECK: call void @llvm.va_end + __builtin_va_end(arglist); + // CHECK: call void @objc_release + // CHECK: ret void +} + +// PR10228 +@interface Test55Base @end +@interface Test55 : Test55Base @end +@implementation Test55 (Category) +- (void) dealloc {} +@end +// CHECK: define internal void @"\01-[Test55(Category) dealloc]"( +// CHECK-NOT: ret +// CHECK: call void bitcast (i8* ({{%.*}}*, i8*, ...)* @objc_msgSendSuper2 to void ({{%.*}}*, i8*)*)( diff --git a/test/CodeGenObjC/autorelease.m b/test/CodeGenObjC/autorelease.m new file mode 100644 index 000000000000..7bf40f11ffd1 --- /dev/null +++ b/test/CodeGenObjC/autorelease.m @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-nonfragile-abi -fobjc-runtime-has-arc -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -emit-llvm -fobjc-nonfragile-abi -fobjc-runtime-has-arc -o - %s | FileCheck %s +// rdar://8881826 +// rdar://9412038 + +@interface I +{ + id ivar; +} +- (id) Meth; ++ (id) MyAlloc;; +@end + +@implementation I +- (id) Meth { + @autoreleasepool { + id p = [I MyAlloc]; + if (!p) + return ivar; + } + return 0; +} ++ (id) MyAlloc { + return 0; +} +@end + +// CHECK: call i8* @objc_autoreleasePoolPush +// CHECK: [[T:%.*]] = load i8** [[A:%.*]] +// CHECK: call void @objc_autoreleasePoolPop diff --git a/test/CodeGenObjC/block-6.m b/test/CodeGenObjC/block-6.m index 3720a87b9e56..44c7a78e698e 100644 --- a/test/CodeGenObjC/block-6.m +++ b/test/CodeGenObjC/block-6.m @@ -2,10 +2,16 @@ // rdar://8893785 void MYFUNC() { -// CHECK: [[T1:%.*]] = bitcast i8* ()* -// CHECK-NEXT: [[FORWARDING:%.*]] = getelementptr inbounds [[N_T:%.*]]* [[N:%.*]], i32 0, i32 1 -// CHECK-NEXT: [[T0:%.*]] = load [[N_T]]** [[FORWARDING]] -// CHECK-NEXT: [[OBSERVER:%.*]] = getelementptr inbounds [[N_T]]* [[T0]], i32 0, i32 6 +// CHECK: define void @MYFUNC() +// CHECK: [[OBSERVER_SLOT:%.*]] = alloca [[OBSERVER_T:%.*]], align 8 + +// CHECK: [[T0:%.*]] = getelementptr inbounds [[OBSERVER_T]]* [[OBSERVER_SLOT]], i32 0, i32 1 +// CHECK: store [[OBSERVER_T]]* [[OBSERVER_SLOT]], [[OBSERVER_T]]** [[T0]] + +// CHECK: [[T1:%.*]] = bitcast i8* ()* +// CHECK: [[FORWARDING:%.*]] = getelementptr inbounds [[OBSERVER_T]]* [[OBSERVER_SLOT]], i32 0, i32 1 +// CHECK-NEXT: [[T0:%.*]] = load [[OBSERVER_T]]** [[FORWARDING]] +// CHECK-NEXT: [[OBSERVER:%.*]] = getelementptr inbounds [[OBSERVER_T]]* [[T0]], i32 0, i32 6 // CHECK-NEXT: store i8* [[T1]], i8** [[OBSERVER]] __block id observer = ^{ return observer; }; } diff --git a/test/CodeGenObjC/blocks.m b/test/CodeGenObjC/blocks.m index 151c16234214..47d47cca2e8c 100644 --- a/test/CodeGenObjC/blocks.m +++ b/test/CodeGenObjC/blocks.m @@ -46,7 +46,7 @@ void test2(Test2 *x) { // CHECK: define void @test2( // CHECK: [[X:%.*]] = alloca [[TEST2:%.*]]*, // CHECK-NEXT: [[WEAKX:%.*]] = alloca [[WEAK_T:%.*]], - // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:%.*]], + // CHECK-NEXT: [[BLOCK:%.*]] = alloca [[BLOCK_T:<{.*}>]], // CHECK-NEXT: store [[TEST2]]* // isa=1 for weak byrefs. @@ -95,8 +95,8 @@ void test2(Test2 *x) { // CHECK: [[BLOCK:%.*]] = bitcast i8* {{%.*}} to [[BLOCK_T]]* // CHECK-NEXT: [[T0:%.*]] = getelementptr inbounds [[BLOCK_T]]* [[BLOCK]], i32 0, i32 5 // CHECK-NEXT: [[T1:%.*]] = load i8** [[T0]] -// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[WEAK_T]]* -// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds [[WEAK_T]]* [[T2]], i32 0, i32 1 -// CHECK-NEXT: [[T4:%.*]] = load [[WEAK_T]]** [[T3]] -// CHECK-NEXT: [[WEAKX:%.*]] = getelementptr inbounds [[WEAK_T]]* [[T4]], i32 0, i32 6 +// CHECK-NEXT: [[T2:%.*]] = bitcast i8* [[T1]] to [[WEAK_T]]{{.*}}* +// CHECK-NEXT: [[T3:%.*]] = getelementptr inbounds [[WEAK_T]]{{.*}}* [[T2]], i32 0, i32 1 +// CHECK-NEXT: [[T4:%.*]] = load [[WEAK_T]]{{.*}}** [[T3]] +// CHECK-NEXT: [[WEAKX:%.*]] = getelementptr inbounds [[WEAK_T]]{{.*}}* [[T4]], i32 0, i32 6 // CHECK-NEXT: [[T0:%.*]] = load [[TEST2]]** [[WEAKX]], align 4 diff --git a/test/CodeGenObjC/debug-info-foreach.m b/test/CodeGenObjC/debug-info-foreach.m deleted file mode 100644 index 89b409c7169e..000000000000 --- a/test/CodeGenObjC/debug-info-foreach.m +++ /dev/null @@ -1,20 +0,0 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -fobjc-default-synthesize-properties -emit-llvm -g %s -o %t -// RUN: grep DW_TAG_lexical_block %t | count 5 -// rdar://8757124 - -@class NSArray; - -int i; -void f(NSArray *a) { - id keys; - for (id thisKey in keys) { - int j = i; - ++j; - i = j; - } - for (id thisKey in keys) { - int k = i; - ++k; - i = k; - } -} diff --git a/test/CodeGenObjC/encode-test.m b/test/CodeGenObjC/encode-test.m index ade0c618225f..f30fb26a1e65 100644 --- a/test/CodeGenObjC/encode-test.m +++ b/test/CodeGenObjC/encode-test.m @@ -161,3 +161,7 @@ struct f // CHECK: @g10 = constant [14 x i8] c"{f=i[0{?=}]i}\00" const char g10[] = @encode(struct f); + +// rdar://9622422 +// CHECK: @g11 = constant [2 x i8] c"v\00" +const char g11[] = @encode(void); diff --git a/test/CodeGenObjC/exceptions-nonfragile.m b/test/CodeGenObjC/exceptions-nonfragile.m index 280b5d416ffa..2557aab25219 100644 --- a/test/CodeGenObjC/exceptions-nonfragile.m +++ b/test/CodeGenObjC/exceptions-nonfragile.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fexceptions -fobjc-exceptions -O2 -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fexceptions -fobjc-exceptions -o - %s | FileCheck %s // rdar://problem/8535238 // CHECK: declare void @objc_exception_rethrow() @@ -15,3 +15,17 @@ void protos() { void throwing() { @throw(@"error!"); } + +// rdar://problem/9431547 +void die(void) __attribute__((nothrow, noreturn)); +void test2(void) { + @try { + die(); + } @finally { + extern void test2_helper(void); + test2_helper(); + } + + // CHECK: define void @test2() + // CHECK-NOT: call void @test2_helper() +} diff --git a/test/CodeGenObjC/gc.m b/test/CodeGenObjC/gc.m new file mode 100644 index 000000000000..93554e65b880 --- /dev/null +++ b/test/CodeGenObjC/gc.m @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-gc -fobjc-nonfragile-abi -emit-llvm -o - %s | FileCheck %s + +void test0(void) { + extern id test0_helper(void); + __attribute__((objc_precise_lifetime)) id x = test0_helper(); + test0_helper(); + // CHECK: define void @test0() + // CHECK: [[T0:%.*]] = call i8* @test0_helper() + // CHECK-NEXT: store i8* [[T0]], i8** [[X:%.*]], align 8 + // CHECK-NEXT: call i8* @test0_helper() + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]], align 8 + // CHECK-NEXT: call void asm sideeffect "", "r"(i8* [[T0]]) nounwind + // CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjC/mrr-autorelease.m b/test/CodeGenObjC/mrr-autorelease.m new file mode 100644 index 000000000000..10f549fcec5e --- /dev/null +++ b/test/CodeGenObjC/mrr-autorelease.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple i386-apple-darwin10 -emit-llvm -o - %s | FileCheck %s +// rdar://8881826 +// rdar://9423507 + +@interface I +{ + id ivar; +} +- (id) Meth; +@end + +@implementation I +- (id) Meth { + @autoreleasepool { + } + return 0; +} +@end + +// CHECK-NOT: call i8* @objc_getClass +// CHECK: call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend +// CHECK: call i8* bitcast (i8* (i8*, i8*, ...)* @objc_msgSend +// CHECK: call void bitcast (i8* (i8*, i8*, ...)* @objc_msgSend diff --git a/test/CodeGenObjC/nonlazy-msgSend.m b/test/CodeGenObjC/nonlazy-msgSend.m new file mode 100644 index 000000000000..3d7ba10dc5e8 --- /dev/null +++ b/test/CodeGenObjC/nonlazy-msgSend.m @@ -0,0 +1,6 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin9 -emit-llvm -o %t %s +// RUN: grep -F 'declare i8* @objc_msgSend(i8*, i8*, ...) nonlazybind' %t + +void f0(id x) { + [x foo]; +} diff --git a/test/CodeGenObjC/property-list-in-class.m b/test/CodeGenObjC/property-list-in-class.m index a5d0dc851dc4..05210588ba9b 100644 --- a/test/CodeGenObjC/property-list-in-class.m +++ b/test/CodeGenObjC/property-list-in-class.m @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -emit-llvm -o %t %s -// RUN: grep -F 'l_OBJC_$_PROP_LIST_C2" = internal global %8 { i32 16, i32 3' %t +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fobjc-nonfragile-abi -emit-llvm %s -o - | FileCheck %s +// CHECK: l_OBJC_$_PROP_LIST_C2" = internal global { i32, i32, [3 x %struct._prop_t] } { i32 16, i32 3 @protocol P @property int i; diff --git a/test/CodeGenObjC/related-result-type.m b/test/CodeGenObjC/related-result-type.m new file mode 100644 index 000000000000..ef38661f94c0 --- /dev/null +++ b/test/CodeGenObjC/related-result-type.m @@ -0,0 +1,54 @@ +// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s + +@interface NSObject ++ (id)alloc; +- (id)init; +- (id)retain; +@end + +@interface NSString : NSObject +@end + +// CHECK: define void @test1() +void test1() { + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: bitcast i8* + NSString *str1 = [[[NSString alloc] init] retain]; +} + +// CHECK: define void @test2() +void test2() { + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: {{call.*@objc_msgSend}} + // CHECK: bitcast i8* + NSString *str1 = NSString.alloc.init.retain; +} + +@interface Test2 : NSString +- (id)init; +@end + +@implementation Test2 +// CHECK: define internal i8* @"\01-[Test2 init]" +- (id)init { + // CHECK: {{call.*@objc_msgSendSuper}} + // CHECK-NEXT: bitcast i8* + return [super init]; +} +@end + +@interface Test3 : NSString +- (id)init; +@end + +@implementation Test3 +// CHECK: define internal i8* @"\01-[Test3 init]" +- (id)init { + // CHECK: {{call.*@objc_msgSendSuper}} + // CHECK-NEXT: bitcast i8* + return [super init]; +} +@end diff --git a/test/CodeGenObjC/terminate.m b/test/CodeGenObjC/terminate.m new file mode 100644 index 000000000000..f04eb6a92bb7 --- /dev/null +++ b/test/CodeGenObjC/terminate.m @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fexceptions -fobjc-exceptions -fobjc-runtime-has-terminate -o - %s | FileCheck %s -check-prefix=CHECK-WITH +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fexceptions -fobjc-exceptions -o - %s | FileCheck %s -check-prefix=CHECK-WITHOUT + +void destroy(void**); + +// rdar://problem/9519113 +void test0(void) { + void test0_helper(void); + void *ptr __attribute__((cleanup(destroy))); + test0_helper(); + + // CHECK-WITH: define void @test0() + // CHECK-WITH: [[PTR:%.*]] = alloca i8*, + // CHECK-WITH: call void @destroy(i8** [[PTR]]) + // CHECK-WITH-NEXT: ret void + // CHECK-WITH: invoke void @destroy(i8** [[PTR]]) + // CHECK-WITH: call i8* @llvm.eh.exception() + // CHECK-WITH-NEXT: @llvm.eh.selector + // CHECK-WITH-NEXT: call void @objc_terminate() + + // CHECK-WITHOUT: define void @test0() + // CHECK-WITHOUT: [[PTR:%.*]] = alloca i8*, + // CHECK-WITHOUT: call void @destroy(i8** [[PTR]]) + // CHECK-WITHOUT-NEXT: ret void + // CHECK-WITHOUT: invoke void @destroy(i8** [[PTR]]) + // CHECK-WITHOUT: call i8* @llvm.eh.exception() + // CHECK-WITHOUT-NEXT: @llvm.eh.selector + // CHECK-WITHOUT-NEXT: call void @abort() +} diff --git a/test/CodeGenObjC/variadic-sends.m b/test/CodeGenObjC/variadic-sends.m index ea13823bc719..6b04b50ca152 100644 --- a/test/CodeGenObjC/variadic-sends.m +++ b/test/CodeGenObjC/variadic-sends.m @@ -28,13 +28,13 @@ void f2(A *a) { @interface B : A @end @implementation B : A -(void) foo { - // CHECK-X86-32: call void bitcast (i8* (%struct._objc_method_description*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_method_description*, i8*, i32)*) - // CHECK-X86-64: call void bitcast (i8* (%struct._objc_method_description*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_method_description*, i8*, i32)*) + // CHECK-X86-32: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_super*, i8*, i32)*) + // CHECK-X86-64: call void bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_super*, i8*, i32)*) [super im1: 1]; } -(void) bar { - // CHECK-X86-32: call void (%struct._objc_method_description*, i8*, i32, i32, ...)* bitcast (i8* (%struct._objc_method_description*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_method_description*, i8*, i32, i32, ...)*) - // CHECK-X86-64: call void (%struct._objc_method_description*, i8*, i32, i32, ...)* bitcast (i8* (%struct._objc_method_description*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_method_description*, i8*, i32, i32, ...)*) + // CHECK-X86-32: call void (%struct._objc_super*, i8*, i32, i32, ...)* bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_super*, i8*, i32, i32, ...)*) + // CHECK-X86-64: call void (%struct._objc_super*, i8*, i32, i32, ...)* bitcast (i8* (%struct._objc_super*, i8*, ...)* @objc_msgSendSuper to void (%struct._objc_super*, i8*, i32, i32, ...)*) [super im2: 1, 2]; } diff --git a/test/CodeGenObjCXX/arc-globals.mm b/test/CodeGenObjCXX/arc-globals.mm new file mode 100644 index 000000000000..7167dbc366d0 --- /dev/null +++ b/test/CodeGenObjCXX/arc-globals.mm @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +// Test that we're properly retaining lifetime-qualified pointers +// initialized statically and wrapping up those initialization in an +// autorelease pool. +id getObject(); + +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call i8* @_Z9getObjectv +// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue +// CHECK-NEXT: {{store i8*.*@global_obj}} +// CHECK-NEXT: ret void +id global_obj = getObject(); + +// CHECK: define internal void @__cxx_global_var_init +// CHECK: call i8* @_Z9getObjectv +// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue +// CHECK-NEXT: {{store i8*.*@global_obj2}} +// CHECK-NEXT: ret void +id global_obj2 = getObject(); + +// CHECK: define internal void @_GLOBAL__I_a +// CHECK: call i8* @objc_autoreleasePoolPush() +// CHECK-NEXT: call void @__cxx_global_var_init +// CHECK-NEXT: call void @__cxx_global_var_init1 +// CHECK-NEXT: call void @objc_autoreleasePoolPop( +// CHECK-NEXT: ret void diff --git a/test/CodeGenObjCXX/arc-mangle.mm b/test/CodeGenObjCXX/arc-mangle.mm new file mode 100644 index 000000000000..1955348f1c32 --- /dev/null +++ b/test/CodeGenObjCXX/arc-mangle.mm @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fobjc-runtime-has-weak -emit-llvm -o - %s | FileCheck %s + +// CHECK: define void @_Z1fPU8__strongP11objc_object(i8**) +void f(__strong id *) {} +// CHECK: define void @_Z1fPU6__weakP11objc_object(i8**) +void f(__weak id *) {} +// CHECK: define void @_Z1fPU15__autoreleasingP11objc_object(i8**) +void f(__autoreleasing id *) {} +// CHECK: define void @_Z1fPP11objc_object(i8**) +void f(__unsafe_unretained id *) {} +// CHECK: define void @_Z1fPKU8__strongP11objc_object(i8**) +void f(const __strong id *) {} +// CHECK: define void @_Z1fPKU6__weakP11objc_object(i8**) +void f(const __weak id *) {} +// CHECK: define void @_Z1fPKU15__autoreleasingP11objc_object(i8**) +void f(const __autoreleasing id *) {} +// CHECK: define void @_Z1fPKP11objc_object(i8**) +void f(const __unsafe_unretained id *) {} + + +template<unsigned N> struct unsigned_c { }; + +// CHECK: define weak_odr void @_Z1gIKvEvP10unsigned_cIXplszv1U8__bridgecvPT_v1U8__bridgecvP11objc_objectcvS3_Li0ELi1EEE +template<typename T>void g(unsigned_c<sizeof((__bridge T*)(__bridge id)(T*)0) + 1>*) {} +template void g<const void>(unsigned_c<sizeof(id) + 1> *); diff --git a/test/CodeGenObjCXX/arc-move.mm b/test/CodeGenObjCXX/arc-move.mm new file mode 100644 index 000000000000..70469e6a8586 --- /dev/null +++ b/test/CodeGenObjCXX/arc-move.mm @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fblocks -fobjc-arc -O2 -std=c++0x -disable-llvm-optzns -o - %s | FileCheck %s + +// define void @_Z11simple_moveRU8__strongP11objc_objectS2_ +void simple_move(__strong id &x, __strong id &y) { + // CHECK: = load i8** + // CHECK: store i8* null + // CHECK: = load i8** + // CHECK: store i8* + // CHECK-NEXT: call void @objc_release + x = static_cast<__strong id&&>(y); + // CHECK-NEXT: ret void +} + +template<typename T> +struct remove_reference { + typedef T type; +}; + +template<typename T> +struct remove_reference<T&> { + typedef T type; +}; + +template<typename T> +struct remove_reference<T&&> { + typedef T type; +}; + +template<typename T> +typename remove_reference<T>::type&& move(T &&x) { + return static_cast<typename remove_reference<T>::type&&>(x); +} + +// CHECK: define void @_Z12library_moveRU8__strongP11objc_objectS2_ +void library_move(__strong id &x, __strong id &y) { + // CHECK: call i8** @_Z4moveIRU8__strongP11objc_objectEON16remove_referenceIT_E4typeEOS5_ + // CHECK: load i8** + // CHECK: store i8* null, i8** + // CHECK: load i8*** + // CHECK-NEXT: load i8** + // CHECK-NEXT: store i8* + // CHECK-NEXT: call void @objc_release + // CHECK-NEXT: ret void + x = move(y); +} + +// CHECK: define void @_Z12library_moveRU8__strongP11objc_object +void library_move(__strong id &y) { + // CHECK: [[Y:%[a-zA-Z0-9]+]] = call i8** @_Z4moveIRU8__strongP11objc_objectEON16remove_referenceIT_E4typeEOS5_ + // Load the object + // CHECK-NEXT: [[OBJ:%[a-zA-Z0-9]+]] = load i8** [[Y]] + // Null out y + // CHECK-NEXT: store i8* null, i8** [[Y]] + // Initialize x with the object + // CHECK-NEXT: store i8* [[OBJ]], i8** [[X:%[a-zA-Z0-9]+]] + id x = move(y); + + // CHECK-NEXT: store i32 17 + int i = 17; + // CHECK-NEXT: [[OBJ:%[a-zA-Z0-9]+]] = load i8** [[X]] + // CHECK-NEXT: call void @objc_release(i8* [[OBJ]]) + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z10const_moveRKU8__strongP11objc_object( +void const_move(const __strong id &x) { + // CHECK: [[Y:%.*]] = alloca i8*, + // CHECK: [[X:%.*]] = call i8** @_Z4moveIRKU8__strongP11objc_objectEON16remove_referenceIT_E4typeEOS5_( + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: store i8* [[T1]], i8** [[Y]] + // CHECK-NEXT: [[T0:%.*]] = load i8** [[Y]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + id y = move(x); +} diff --git a/test/CodeGenObjCXX/arc-new-delete.mm b/test/CodeGenObjCXX/arc-new-delete.mm new file mode 100644 index 000000000000..4597985f8dfe --- /dev/null +++ b/test/CodeGenObjCXX/arc-new-delete.mm @@ -0,0 +1,95 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fobjc-runtime-has-weak -fblocks -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s | FileCheck %s + +typedef __strong id strong_id; +typedef __weak id weak_id; + +// CHECK: define void @_Z8test_newP11objc_object +void test_new(id invalue) { + // CHECK: alloca i8* + // CHECK-NEXT: call i8* @objc_retain + + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new strong_id; + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new weak_id; + + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new __strong id; + // CHECK: call noalias i8* @_Znwm + // CHECK-NEXT: {{bitcast i8\*.*to i8\*\*}} + // CHECK-NEXT: store i8* null, i8** + new __weak id; + + // CHECK: call noalias i8* @_Znwm + // CHECK: call i8* @objc_retain + // CHECK: store i8* + new __strong id(invalue); + + // CHECK: call noalias i8* @_Znwm + // CHECK: call i8* @objc_initWeak + new __weak id(invalue); + + // CHECK: call void @objc_release + // CHECK: ret void +} + +// CHECK: define void @_Z14test_array_new +void test_array_new() { + // CHECK: call noalias i8* @_Znam + // CHECK: store i64 17, i64* + // CHECK: call void @llvm.memset.p0i8.i64 + new strong_id[17]; + + // CHECK: call noalias i8* @_Znam + // CHECK: store i64 17, i64* + // CHECK: call void @llvm.memset.p0i8.i64 + new weak_id[17]; + // CHECK: ret void +} + +// CHECK: define void @_Z11test_deletePU8__strongP11objc_objectPU6__weakS0_ +void test_delete(__strong id *sptr, __weak id *wptr) { + // CHECK: br i1 + // CHECK: load i8** + // CHECK-NEXT: call void @objc_release + // CHECK: call void @_ZdlPv + delete sptr; + + // CHECK: call void @objc_destroyWeak + // CHECK: call void @_ZdlPv + delete wptr; + + // CHECK: ret void +} + +// CHECK: define void @_Z17test_array_deletePU8__strongP11objc_objectPU6__weakS0_ +void test_array_delete(__strong id *sptr, __weak id *wptr) { + // CHECK: icmp eq i8** [[BEGIN:%.*]], null + // CHECK: [[LEN:%.*]] = load i64* {{%.*}} + // CHECK: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 [[LEN]] + // CHECK-NEXT: icmp eq i8** [[BEGIN]], [[END]] + // CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], + // CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1 + // CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]] + // CHECK-NEXT: call void @objc_release(i8* [[T0]]) + // CHECK-NEXT: icmp eq i8** [[CUR]], [[BEGIN]] + // CHECK: call void @_ZdaPv + delete [] sptr; + + // CHECK: icmp eq i8** [[BEGIN:%.*]], null + // CHECK: [[LEN:%.*]] = load i64* {{%.*}} + // CHECK: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 [[LEN]] + // CHECK-NEXT: icmp eq i8** [[BEGIN]], [[END]] + // CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], + // CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1 + // CHECK-NEXT: call void @objc_destroyWeak(i8** [[CUR]]) + // CHECK-NEXT: icmp eq i8** [[CUR]], [[BEGIN]] + // CHECK: call void @_ZdaPv + delete [] wptr; +} diff --git a/test/CodeGenObjCXX/arc-pseudo-destructors.mm b/test/CodeGenObjCXX/arc-pseudo-destructors.mm new file mode 100644 index 000000000000..4023e90b7dc7 --- /dev/null +++ b/test/CodeGenObjCXX/arc-pseudo-destructors.mm @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fobjc-runtime-has-weak -fblocks -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s | FileCheck %s + +// CHECK: define void @_Z28test_objc_object_pseudo_dtorPU8__strongP11objc_objectPU6__weakS0_ +void test_objc_object_pseudo_dtor(__strong id *ptr, __weak id *wptr) { + // CHECK: load i8*** + // CHECK-NEXT: load i8** + // CHECK-NEXT: call void @objc_release + ptr->~id(); + + // CHECK: call void @objc_destroyWeak(i8** {{%.*}}) + wptr->~id(); + + // CHECK: load i8*** + // CHECK-NEXT: load i8** + // CHECK-NEXT: call void @objc_release + (*ptr).~id(); + + // CHECK: call void @objc_destroyWeak(i8** {{%.*}}) + (*wptr).~id(); + // CHECK: ret void +} diff --git a/test/CodeGenObjCXX/arc-references.mm b/test/CodeGenObjCXX/arc-references.mm new file mode 100644 index 000000000000..3d0313d13ac8 --- /dev/null +++ b/test/CodeGenObjCXX/arc-references.mm @@ -0,0 +1,83 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +@interface A +@end + +id getObject(); +void callee(); + +// Lifetime extension for binding a reference to an rvalue +// CHECK: define void @_Z5test0v() +void test0() { + // CHECK: call i8* @_Z9getObjectv + // CHECK-NEXT:: call i8* @objc_retainAutoreleasedReturnValue + const __strong id &ref1 = getObject(); + // CHECK: call void @_Z6calleev + callee(); + // CHECK: call i8* @_Z9getObjectv + // CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue + // CHECK-NEXT: call i8* @objc_autorelease + const __autoreleasing id &ref2 = getObject(); + // CHECK: call void @_Z6calleev + callee(); + // CHECK: call void @objc_release + // CHECK-NEXT: ret +} + +// No lifetime extension when we're binding a reference to an lvalue. +// CHECK: define void @_Z5test1RU8__strongP11objc_objectRU6__weakS0_ +void test1(__strong id &x, __weak id &y) { + // CHECK-NOT: release + const __strong id &ref1 = x; + const __autoreleasing id &ref2 = x; + const __weak id &ref3 = y; + // CHECK: ret void +} + +typedef __strong id strong_id; + +//CHECK: define void @_Z5test3v +void test3() { + // CHECK: call i8* @objc_initWeak + // CHECK-NEXT: store i8** + const __weak id &ref = strong_id(); + // CHECK-NEXT: call void @_Z6calleev() + callee(); + // CHECK-NEXT: call void @objc_destroyWeak + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z5test4RU8__strongP11objc_object +void test4(__strong id &x) { + // CHECK: call i8* @objc_retain + __strong A* const &ar = x; + // CHECK: store i32 17, i32* + int i = 17; + // CHECK: call void @objc_release( + // CHECK: ret void +} + +void sink(__strong A* &&); + +// CHECK: define void @_Z5test5RU8__strongP11objc_object +void test5(__strong id &x) { + // CHECK: [[OBJ_ID:%[a-zA-Z0-9]+]] = call i8* @objc_retain + // CHECK-NEXT: [[OBJ_A:%[a-zA-Z0-9]+]] = bitcast i8* [[OBJ_ID]] to [[A:%[a-zA-Z0-9]+]]* + // CHECK-NEXT: store [[A]]* [[OBJ_A]], [[A]]** [[REFTMP:%[a-zA-Z0-9]+]] + // CHECK-NEXT: call void @_Z4sinkOU8__strongP1A + sink(x); + // CHECK-NEXT: [[OBJ_A:%[a-zA-Z0-9]+]] = load [[A]]** [[REFTMP]] + // CHECK-NEXT: [[OBJ_ID:%[a-zA-Z0-9]+]] = bitcast [[A]]* [[OBJ_A]] to i8* + // CHECK-NEXT: call void @objc_release + // CHECK-NEXT: store i32 17, i32 + int i = 17; + // CHECK-NEXT: ret void +} + +// CHECK: define internal void @__cxx_global_var_init( +// CHECK: call i8* @_Z9getObjectv +// CHECK-NEXT: call i8* @objc_retainAutoreleasedReturnValue +const __strong id &global_ref = getObject(); + +// Note: we intentionally don't release the object. + diff --git a/test/CodeGenObjCXX/arc-special-member-functions.mm b/test/CodeGenObjCXX/arc-special-member-functions.mm new file mode 100644 index 000000000000..d88a2bd62ffc --- /dev/null +++ b/test/CodeGenObjCXX/arc-special-member-functions.mm @@ -0,0 +1,161 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-arc -fblocks -triple x86_64-apple-darwin10.0.0 -emit-llvm -o - %s | FileCheck %s + +struct ObjCMember { + id member; +}; + +struct ObjCArrayMember { + id member[2][3]; +}; + +struct ObjCBlockMember { + int (^bp)(int); +}; + +// CHECK: define void @_Z42test_ObjCMember_default_construct_destructv( +void test_ObjCMember_default_construct_destruct() { + // CHECK: call void @_ZN10ObjCMemberC1Ev + // CHECK: call void @_ZN10ObjCMemberD1Ev + ObjCMember m1; +} + +// CHECK: define void @_Z39test_ObjCMember_copy_construct_destruct10ObjCMember +void test_ObjCMember_copy_construct_destruct(ObjCMember m1) { + // CHECK: call void @_ZN10ObjCMemberC1ERKS_ + // CHECK: call void @_ZN10ObjCMemberD1Ev + ObjCMember m2 = m1; + // CHECK: ret void +} + +// CHECK: define void @_Z27test_ObjCMember_copy_assign10ObjCMemberS_ +void test_ObjCMember_copy_assign(ObjCMember m1, ObjCMember m2) { + // CHECK: {{call.*_ZN10ObjCMemberaSERKS_}} + m1 = m2; + // CHECK-NEXT: ret void +} + +// Implicitly-generated copy assignment operator for ObjCMember +// CHECK: {{define linkonce_odr.*@_ZN10ObjCMemberaSERKS_}} +// CHECK: call void @objc_storeStrong +// CHECK: ret + +// CHECK: define void @_Z47test_ObjCArrayMember_default_construct_destructv +void test_ObjCArrayMember_default_construct_destruct() { + // CHECK: call void @_ZN15ObjCArrayMemberC1Ev + ObjCArrayMember m1; + // CHECK: call void @_ZN15ObjCArrayMemberD1Ev + // CHECK: ret void +} + +// CHECK: define void @_Z44test_ObjCArrayMember_copy_construct_destruct15ObjCArrayMember +void test_ObjCArrayMember_copy_construct_destruct(ObjCArrayMember m1) { + // CHECK: call void @_ZN15ObjCArrayMemberC1ERKS_ + ObjCArrayMember m2 = m1; + // CHECK: call void @_ZN15ObjCArrayMemberD1Ev + // CHECK: ret void +} + +void test_ObjCArrayMember_copy_assign(ObjCArrayMember m1, ObjCArrayMember m2) { + // CHECK: {{call.*@_ZN15ObjCArrayMemberaSERKS_}} + m1 = m2; + // CHECK-NEXT: ret void +} + +// Implicitly-generated copy assignment operator for ObjCArrayMember +// CHECK: {{define linkonce_odr.*@_ZN15ObjCArrayMemberaSERKS_}} +// CHECK: call void @objc_storeStrong +// CHECK-NEXT: br label +// CHECK: ret + +// CHECK: define void @_Z47test_ObjCBlockMember_default_construct_destructv +void test_ObjCBlockMember_default_construct_destruct() { + // CHECK: call void @_ZN15ObjCBlockMemberC1Ev + ObjCBlockMember m; + // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z44test_ObjCBlockMember_copy_construct_destruct15ObjCBlockMember +void test_ObjCBlockMember_copy_construct_destruct(ObjCBlockMember m1) { + // CHECK: call void @_ZN15ObjCBlockMemberC1ERKS_ + ObjCBlockMember m2 = m1; + // CHECK-NEXT: call void @_ZN15ObjCBlockMemberD1Ev + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z32test_ObjCBlockMember_copy_assign15ObjCBlockMemberS_ +void test_ObjCBlockMember_copy_assign(ObjCBlockMember m1, ObjCBlockMember m2) { + // CHECK: {{call.*_ZN15ObjCBlockMemberaSERKS_}} + m1 = m2; + // CHECK-NEXT: ret void +} + +// Implicitly-generated copy assignment operator for ObjCBlockMember +// CHECK: define linkonce_odr {{%.*}}* @_ZN15ObjCBlockMemberaSERKS_( +// CHECK: [[T0:%.*]] = call i8* @objc_retainBlock( +// CHECK-NEXT: [[T1:%.*]] = bitcast i8* [[T0]] to i32 (i32)* +// CHECK-NEXT: [[T2:%.*]] = load {{.*}} [[SLOT:%.*]], +// CHECK: store +// CHECK-NEXT: [[T3:%.*]] = bitcast +// CHECK-NEXT: call void @objc_release(i8* [[T3]]) +// CHECK-NEXT: ret + +// Implicitly-generated copy constructor for ObjCBlockMember +// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberC2ERKS_ +// CHECK: call i8* @objc_retainBlock +// CHECK: ret + +// Implicitly-generated destructor for ObjCBlockMember +// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberD2Ev +// CHECK: call void @objc_release(i8* +// CHECK: ret + +// Implicitly-generated default constructor for ObjCBlockMember +// CHECK: define linkonce_odr void @_ZN15ObjCBlockMemberC2Ev +// CHECK: store {{.*}} null, +// CHECK-NEXT: ret void + +// Implicitly-generated copy constructor for ObjCArrayMember +// CHECK: define linkonce_odr void @_ZN15ObjCArrayMemberC2ERKS_ +// CHECK: br i1 +// CHECK: call i8* @objc_retain +// CHECK-NEXT: store i8* +// CHECK-NEXT: br label +// CHECK: ret + +// Implicitly-generated destructor for ObjCArrayMember +// CHECK: define linkonce_odr void @_ZN15ObjCArrayMemberD2Ev +// CHECK: [[BEGIN:%.*]] = getelementptr inbounds [2 x [3 x i8*]]* +// CHECK-NEXT: [[END:%.*]] = getelementptr inbounds i8** [[BEGIN]], i64 6 +// CHECK-NEXT: br label +// CHECK: [[PAST:%.*]] = phi i8** [ [[END]], {{%.*}} ], [ [[CUR:%.*]], {{%.*}} ] +// CHECK-NEXT: [[CUR]] = getelementptr inbounds i8** [[PAST]], i64 -1 +// CHECK-NEXT: [[T0:%.*]] = load i8** [[CUR]] +// CHECK-NEXT: call void @objc_release(i8* [[T0]]) +// CHECK-NEXT: [[T1:%.*]] = icmp eq i8** [[CUR]], [[BEGIN]] +// CHECK-NEXT: br i1 [[T1]], +// CHECK: ret void + +// Implicitly-generated default constructor for ObjCArrayMember +// CHECK: define linkonce_odr void @_ZN15ObjCArrayMemberC2Ev +// CHECK: call void @llvm.memset.p0i8.i64 +// CHECK: ret + +// Implicitly-generated copy constructor for ObjCMember +// CHECK: define linkonce_odr void @_ZN10ObjCMemberC2ERKS_ +// CHECK-NOT: objc_release +// CHECK: call i8* @objc_retain +// CHECK-NEXT: store i8* +// CHECK-NEXT: ret void + +// Implicitly-generated destructor for ObjCMember +// CHECK: define linkonce_odr void @_ZN10ObjCMemberD2Ev +// CHECK: call void @objc_release +// CHECK: ret void + +// Implicitly-generated default constructor for ObjCMember +// CHECK: define linkonce_odr void @_ZN10ObjCMemberC2Ev +// CHECK-NOT: objc_release +// CHECK: store i8* null +// CHECK-NEXT: ret void + diff --git a/test/CodeGenObjCXX/arc.mm b/test/CodeGenObjCXX/arc.mm new file mode 100644 index 000000000000..0c5466a4cea3 --- /dev/null +++ b/test/CodeGenObjCXX/arc.mm @@ -0,0 +1,166 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fblocks -fobjc-arc -O2 -disable-llvm-optzns -o - %s | FileCheck %s + +// rdar://problem/9315552 +// The analogous ObjC testcase test46 in arr.m. +void test0(__weak id *wp, __weak volatile id *wvp) { + extern id test0_helper(void); + + // TODO: this is sub-optimal, we should retain at the actual call site. + // TODO: in the non-volatile case, we do not need to be reloading. + + // CHECK: [[T0:%.*]] = call i8* @_Z12test0_helperv() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_retain(i8* [[T3]]) + // CHECK-NEXT: store i8* [[T4]], i8** + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + id x = *wp = test0_helper(); + + // CHECK: [[T0:%.*]] = call i8* @_Z12test0_helperv() + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retainAutoreleasedReturnValue(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8*** {{%.*}}, align 8 + // CHECK-NEXT: [[T3:%.*]] = call i8* @objc_storeWeak(i8** [[T2]], i8* [[T1]]) + // CHECK-NEXT: [[T4:%.*]] = call i8* @objc_loadWeakRetained(i8** [[T2]]) + // CHECK-NEXT: store i8* [[T4]], i8** + // CHECK-NEXT: call void @objc_release(i8* [[T1]]) + id y = *wvp = test0_helper(); +} + +// rdar://problem/9320648 +struct Test1_helper { Test1_helper(); }; +@interface Test1 @end +@implementation Test1 { Test1_helper x; } @end +// CHECK: define internal i8* @"\01-[Test1 .cxx_construct]"( +// CHECK: call void @_ZN12Test1_helperC1Ev( +// CHECK-NEXT: load +// CHECK-NEXT: bitcast +// CHECK-NEXT: ret i8* + +void test34(int cond) { + __strong id strong; + __weak id weak; + extern void test34_sink(id *); + test34_sink(cond ? &strong : 0); + test34_sink(cond ? &weak : 0); + + // CHECK: define void @_Z6test34i( + // CHECK: [[COND:%.*]] = alloca i32 + // CHECK-NEXT: [[STRONG:%.*]] = alloca i8* + // CHECK-NEXT: [[WEAK:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP1:%.*]] = alloca i8* + // CHECK-NEXT: [[TEMP2:%.*]] = alloca i8* + // CHECK-NEXT: store i32 + // CHECK-NEXT: store i8* null, i8** [[STRONG]] + // CHECK-NEXT: call i8* @objc_initWeak(i8** [[WEAK]], i8* null) + + // CHECK-NEXT: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP1]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP1]] + // CHECK-NEXT: br label + // CHECK: call void @_Z11test34_sinkPU15__autoreleasingP11objc_object(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP1]] + // CHECK-NEXT: [[T1:%.*]] = call i8* @objc_retain(i8* [[T0]]) + // CHECK-NEXT: [[T2:%.*]] = load i8** [[ARG]] + // CHECK-NEXT: store i8* [[T1]], i8** [[ARG]] + // CHECK-NEXT: call void @objc_release(i8* [[T2]]) + // CHECK-NEXT: br label + + // CHECK: [[T0:%.*]] = load i32* [[COND]] + // CHECK-NEXT: [[T1:%.*]] = icmp ne i32 [[T0]], 0 + // CHECK: [[ARG:%.*]] = phi i8** + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: [[T1:%.*]] = select i1 [[T0]], i8** null, i8** [[TEMP2]] + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = call i8* @objc_loadWeak(i8** [[ARG]]) + // CHECK-NEXT: store i8* [[T0]], i8** [[TEMP2]] + // CHECK-NEXT: br label + // CHECK: call void @_Z11test34_sinkPU15__autoreleasingP11objc_object(i8** [[T1]]) + // CHECK-NEXT: [[T0:%.*]] = icmp eq i8** [[ARG]], null + // CHECK-NEXT: br i1 [[T0]], + // CHECK: [[T0:%.*]] = load i8** [[TEMP2]] + // CHECK-NEXT: call i8* @objc_storeWeak(i8** [[ARG]], i8* [[T0]]) + // CHECK-NEXT: br label + + // CHECK: call void @objc_destroyWeak(i8** [[WEAK]]) + // CHECK: ret void +} + +struct Test35_Helper { + static id makeObject1() __attribute__((ns_returns_retained)); + id makeObject2() __attribute__((ns_returns_retained)); + static id makeObject3(); + id makeObject4(); +}; + +// CHECK: define void @_Z6test3513Test35_HelperPS_ +void test35(Test35_Helper x0, Test35_Helper *x0p) { + // CHECK: call i8* @_ZN13Test35_Helper11makeObject1Ev + // CHECK-NOT: call i8* @objc_retain + id obj1 = Test35_Helper::makeObject1(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject2Ev + // CHECK-NOT: call i8* @objc_retain + id obj2 = x0.makeObject2(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject2Ev + // CHECK-NOT: call i8* @objc_retain + id obj3 = x0p->makeObject2(); + id (Test35_Helper::*pmf)() __attribute__((ns_returns_retained)) + = &Test35_Helper::makeObject2; + // CHECK: call i8* % + // CHECK-NOT: call i8* @objc_retain + id obj4 = (x0.*pmf)(); + // CHECK: call i8* % + // CHECK-NOT: call i8* @objc_retain + id obj5 = (x0p->*pmf)(); + + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// CHECK: define void @_Z7test35b13Test35_HelperPS_ +void test35b(Test35_Helper x0, Test35_Helper *x0p) { + // CHECK: call i8* @_ZN13Test35_Helper11makeObject3Ev + // CHECK: call i8* @objc_retain + id obj1 = Test35_Helper::makeObject3(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject4Ev + // CHECK: call i8* @objc_retain + id obj2 = x0.makeObject4(); + // CHECK: call i8* @_ZN13Test35_Helper11makeObject4Ev + // CHECK: call i8* @objc_retain + id obj3 = x0p->makeObject4(); + id (Test35_Helper::*pmf)() = &Test35_Helper::makeObject4; + // CHECK: call i8* % + // CHECK: call i8* @objc_retain + id obj4 = (x0.*pmf)(); + // CHECK: call i8* % + // CHECK: call i8* @objc_retain + id obj5 = (x0p->*pmf)(); + + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK: call void @objc_release + // CHECK-NEXT: ret void +} + +// rdar://problem/9603128 +// CHECK: define i8* @_Z6test36P11objc_object( +id test36(id z) { + // CHECK: objc_retain + // CHECK: objc_retain + // CHECK: objc_release + // CHECK: objc_autoreleaseReturnValue + return z; +} diff --git a/test/CodeGenObjCXX/catch-id-type.mm b/test/CodeGenObjCXX/catch-id-type.mm new file mode 100644 index 000000000000..ece342bb8720 --- /dev/null +++ b/test/CodeGenObjCXX/catch-id-type.mm @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple i386-apple-macosx10.6.6 -emit-llvm -fobjc-exceptions -fcxx-exceptions -fexceptions -o - %s | FileCheck %s +// rdar://8940528 + +@interface ns_array ++ (id) array; +@end + +@implementation ns_array ++ (id) array { return 0; } +@end + +id Groups(); + +@protocol P @end; + +@interface INTF<P> { + double dd; +} +@end + +id FUNC() { + id groups; + try + { + groups = Groups(); // throws on errors. + } + catch( INTF<P>* error ) + { + Groups(); + } + catch( id error ) + { + // CHECK: call i32 (i8*, i8*, ...)* @llvm.eh.selector({{.*}} @__gxx_personality_v0 {{.*}} @_ZTIP4INTF {{.*}} @_ZTIP11objc_object {{.*}} @_ZTIP10objc_class + error = error; + groups = [ns_array array]; + } + catch (Class cl) { + cl = cl; + groups = [ns_array array]; + } + return groups; + +} + +int main() { + FUNC(); + return 0; +} diff --git a/test/CodeGenObjCXX/copy.mm b/test/CodeGenObjCXX/copy.mm new file mode 100644 index 000000000000..133910f25dcf --- /dev/null +++ b/test/CodeGenObjCXX/copy.mm @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s + +// rdar://problem/9158302 +// This should not use a memmove_collectable in non-GC mode. +namespace test0 { + struct A { + id x; + }; + + // CHECK: define [[A:%.*]]* @_ZN5test04testENS_1AE( + // CHECK: alloca + // CHECK-NEXT: getelementptr + // CHECK-NEXT: store + // CHECK-NEXT: call noalias i8* @_Znwm( + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @llvm.memset.p0i8.i64( + // CHECK-NEXT: bitcast + // CHECK-NEXT: bitcast + // CHECK-NEXT: call void @llvm.memcpy.p0i8.p0i8.i64( + // CHECK-NEXT: ret + A *test(A a) { + return new A(a); + } +} + diff --git a/test/CodeGenObjCXX/encode.mm b/test/CodeGenObjCXX/encode.mm index dce3d70665de..2c10fbcb9753 100644 --- a/test/CodeGenObjCXX/encode.mm +++ b/test/CodeGenObjCXX/encode.mm @@ -91,6 +91,20 @@ namespace rdar9357400 { const char gg[] = @encode(vector4f); } +// rdar://9624314 +namespace rdar9624314 { + struct B2 { int x; }; + struct B3 {}; + struct S : B2, B3 {}; + + // CHECK: @_ZN11rdar9624314L2ggE = internal constant [6 x i8] c"{S=i}\00" + const char gg[] = @encode(S); + + struct S2 { unsigned : 0; int x; unsigned : 0; }; + // CHECK: @_ZN11rdar9624314L2g2E = internal constant [11 x i8] c"{S2=b0ib0}\00" + const char g2[] = @encode(S2); +} + struct Base1 { char x; }; diff --git a/test/CodeGenObjCXX/gc.mm b/test/CodeGenObjCXX/gc.mm new file mode 100644 index 000000000000..aa293dacf3a7 --- /dev/null +++ b/test/CodeGenObjCXX/gc.mm @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -fobjc-gc -triple x86_64-apple-darwin10 -emit-llvm -o - %s | FileCheck %s + +namespace test0 { + extern id x; + + struct A { + id x; + A(); + }; + A::A() : x(test0::x) {} + +// CHECK: define void @_ZN5test01AC2Ev( +// CHECK: [[THIS:%.*]] = alloca [[TEST0:%.*]]*, align 8 +// CHECK-NEXT: store +// CHECK-NEXT: [[T0:%.*]] = load [[TEST0]]** [[THIS]] +// CHECK-NEXT: [[T1:%.*]] = getelementptr inbounds [[TEST0]]* [[T0]], i32 0, i32 0 +// CHECK-NEXT: [[T2:%.*]] = load i8** @_ZN5test01xE +// CHECK-NEXT: call i8* @objc_assign_strongCast(i8* [[T2]], i8** [[T1]]) +// CHECK-NEXT: ret void +} diff --git a/test/CodeGenObjCXX/property-object-conditional-exp.mm b/test/CodeGenObjCXX/property-object-conditional-exp.mm index a3c1027ed709..5d8a8826355c 100644 --- a/test/CodeGenObjCXX/property-object-conditional-exp.mm +++ b/test/CodeGenObjCXX/property-object-conditional-exp.mm @@ -22,11 +22,12 @@ extern "C" bool CGRectIsEmpty(CGRect); CGRect dataRect; CGRect virtualBounds; -// CHECK: [[SRC:%.*]] = call %struct.CGRect bitcast (i8* (i8*, i8*, ...)* @objc_msgSend -// CHECK-NEXT:getelementptr %struct.CGRect* [[SRC:%.*]] +// CHECK: [[SRC:%.*]] = call { i8*, i32 } bitcast (i8* (i8*, i8*, ...)* @objc_msgSend +// CHECK-NEXT: bitcast +// CHECK-NEXT:getelementptr { i8*, i32 }* [[SRC:%.*]] // CHECK-NEXT:extractvalue // CHECK-NEXT:store -// CHECK-NEXT:getelementptr %struct.CGRect* [[SRC:%.*]] +// CHECK-NEXT:getelementptr { i8*, i32 }* [[SRC:%.*]] // CHECK-NEXT:extractvalue // CHECK-NEXT:store dataRect = CGRectIsEmpty(virtualBounds) ? self.bounds : virtualBounds; diff --git a/test/CodeGenObjCXX/property-objects.mm b/test/CodeGenObjCXX/property-objects.mm index 8e98b0dae7e4..1f4311763595 100644 --- a/test/CodeGenObjCXX/property-objects.mm +++ b/test/CodeGenObjCXX/property-objects.mm @@ -2,7 +2,7 @@ // CHECK-NOT: callq _objc_msgSend_stret // CHECK: call void @_ZN1SC1ERKS_ // CHECK: call %class.S* @_ZN1SaSERKS_ -// CHECK: call %class.S* @_ZN6CGRectaSERKS_ +// CHECK: call %struct.CGRect* @_ZN6CGRectaSERKS_ class S { public: diff --git a/test/CodeGenOpenCL/2011-04-15-vec-init-from-vec.cl b/test/CodeGenOpenCL/2011-04-15-vec-init-from-vec.cl index c61be69b5739..fbe3d89f10b2 100644 --- a/test/CodeGenOpenCL/2011-04-15-vec-init-from-vec.cl +++ b/test/CodeGenOpenCL/2011-04-15-vec-init-from-vec.cl @@ -1,12 +1,11 @@ // RUN: %clang_cc1 %s -emit-llvm -o %t typedef __attribute__((ext_vector_type(4))) unsigned char uchar4; -typedef __attribute__((ext_vector_type(4))) unsigned int int4; typedef __attribute__((ext_vector_type(8))) unsigned char uchar8; // OpenCL allows vectors to be initialized by vectors Handle bug in // VisitInitListExpr for this case below. -void foo( int4 v ) +void foo( uchar8 x ) { - uchar4 val[4] = {{(uchar4){((uchar8)(v.lo)).lo}}}; -}
\ No newline at end of file + uchar4 val[4] = {{(uchar4){x.lo}}}; +} diff --git a/test/CodeGenOpenCL/vector_literals_valid.cl b/test/CodeGenOpenCL/vector_literals_valid.cl new file mode 100644 index 000000000000..bba5b23e23bd --- /dev/null +++ b/test/CodeGenOpenCL/vector_literals_valid.cl @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -emit-llvm %s -o %t + +typedef __attribute__(( ext_vector_type(2) )) int int2; +typedef __attribute__(( ext_vector_type(3) )) int int3; +typedef __attribute__(( ext_vector_type(4) )) int int4; +typedef __attribute__(( ext_vector_type(8) )) int int8; +typedef __attribute__(( ext_vector_type(4) )) float float4; + +void vector_literals_valid() { + int4 a_1_1_1_1 = (int4)(1,2,3,4); + int4 a_2_1_1 = (int4)((int2)(1,2),3,4); + int4 a_1_2_1 = (int4)(1,(int2)(2,3),4); + int4 a_1_1_2 = (int4)(1,2,(int2)(3,4)); + int4 a_2_2 = (int4)((int2)(1,2),(int2)(3,4)); + int4 a_3_1 = (int4)((int3)(1,2,3),4); + int4 a_1_3 = (int4)(1,(int3)(2,3,4)); + int4 a = (int4)(1); + int8 b = (int8)(1,2,a.xy,a); + float4 V2 = (float4) (1); +} + + diff --git a/test/Driver/arc-exceptions.m b/test/Driver/arc-exceptions.m new file mode 100644 index 000000000000..4501ccd07382 --- /dev/null +++ b/test/Driver/arc-exceptions.m @@ -0,0 +1,5 @@ +// RUN: %clang -### -x objective-c -arch x86_64 -fobjc-arc -fsyntax-only %s 2> %t.log +// RUN: grep objective-c %t.log +// RUN: not grep "fobjc-arc-exceptions" %t.log +// RUN: %clang -### -x objective-c++ -arch x86_64 -fobjc-arc -fsyntax-only %s 2> %t.log +// RUN: grep "fobjc-arc-exceptions" %t.log diff --git a/test/Driver/arc.c b/test/Driver/arc.c new file mode 100644 index 000000000000..a4d4ed1c6043 --- /dev/null +++ b/test/Driver/arc.c @@ -0,0 +1,14 @@ +// RUN: %clang -ObjC -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -x objective-c -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -x objective-c++ -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck %s +// RUN: %clang -x c -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck -check-prefix NOTOBJC %s +// RUN: %clang -x c++ -ccc-host-triple i386-apple-darwin9 -m32 -fobjc-arc %s -fsyntax-only 2>&1 | FileCheck -check-prefix NOTOBJC %s + +// Just to test clang is working. +# foo + +// CHECK: error: -fobjc-arc is not supported with fragile abi +// CHECK-NOT: invalid preprocessing directive + +// NOTOBJC-NOT: error: -fobjc-arc is not supported with fragile abi +// NOTOBJC: invalid preprocessing directive diff --git a/test/Driver/darwin-ld.c b/test/Driver/darwin-ld.c index 91d47eaaf412..33b48583b5e9 100644 --- a/test/Driver/darwin-ld.c +++ b/test/Driver/darwin-ld.c @@ -18,9 +18,9 @@ // // Note that at conception, this exactly matches gcc. -// RUN: %clang -ccc-host-triple i386-apple-darwin9 -### -A ARG0 -F ARG1 -L ARG2 -Mach -T ARG4 -X -Z -all_load -allowable_client ARG8 -bind_at_load -compatibility_version ARG11 -current_version ARG12 -d -dead_strip -dylib_file ARG14 -dylinker -dylinker_install_name ARG16 -dynamic -dynamiclib -e ARG19 -exported_symbols_list ARG20 -fexceptions -flat_namespace -fnested-functions -fopenmp -force_cpusubtype_ALL -fpie -fprofile-arcs -headerpad_max_install_names -image_base ARG29 -init ARG30 -install_name ARG31 -m ARG33 -mmacosx-version-min=10.3.2 -multi_module -multiply_defined ARG37 -multiply_defined_unused ARG38 -no_dead_strip_inits_and_terms -nodefaultlibs -nofixprebinding -nomultidefs -noprebind -noseglinkedit -nostartfiles -nostdlib -pagezero_size ARG54 -pg -prebind -prebind_all_twolevel_modules -preload -r -read_only_relocs ARG55 -s -sectalign ARG57_0 ARG57_1 ARG57_2 -sectcreate ARG58_0 ARG58_1 ARG58_2 -sectobjectsymbols ARG59_0 ARG59_1 -sectorder ARG60_0 ARG60_1 ARG60_2 -seg1addr ARG61 -seg_addr_table ARG62 -seg_addr_table_filename ARG63 -segaddr ARG64_0 ARG64_1 -segcreate ARG65_0 ARG65_1 ARG65_2 -seglinkedit -segprot ARG67_0 ARG67_1 ARG67_2 -segs_read_FOO -segs_read_only_addr ARG69 -segs_read_write_addr ARG70 -shared-libgcc -single_module -static -static-libgcc -sub_library ARG77 -sub_umbrella ARG78 -t -twolevel_namespace -twolevel_namespace_hints -u ARG82 -umbrella ARG83 -undefined ARG84 -unexported_symbols_list ARG85 -w -weak_reference_mismatches ARG87 -whatsloaded -whyload -y -filelist FOO -l FOO 2> %t.log +// RUN: %clang -ccc-host-triple i386-apple-darwin9 -### -A ARG0 -F ARG1 -L ARG2 -Mach -T ARG4 -X -Z -all_load -allowable_client ARG8 -bind_at_load -compatibility_version ARG11 -current_version ARG12 -d -dead_strip -dylib_file ARG14 -dylinker -dylinker_install_name ARG16 -dynamic -dynamiclib -e ARG19 -exported_symbols_list ARG20 -fexceptions -flat_namespace -fnested-functions -fopenmp -force_cpusubtype_ALL -force_load ARG28 -fpie -fprofile-arcs -headerpad_max_install_names -image_base ARG29 -init ARG30 -install_name ARG31 -m ARG33 -mmacosx-version-min=10.3.2 -multi_module -multiply_defined ARG37 -multiply_defined_unused ARG38 -no_dead_strip_inits_and_terms -nodefaultlibs -nofixprebinding -nomultidefs -noprebind -noseglinkedit -nostartfiles -nostdlib -pagezero_size ARG54 -pg -prebind -prebind_all_twolevel_modules -preload -r -read_only_relocs ARG55 -s -sectalign ARG57_0 ARG57_1 ARG57_2 -sectcreate ARG58_0 ARG58_1 ARG58_2 -sectobjectsymbols ARG59_0 ARG59_1 -sectorder ARG60_0 ARG60_1 ARG60_2 -seg1addr ARG61 -seg_addr_table ARG62 -seg_addr_table_filename ARG63 -segaddr ARG64_0 ARG64_1 -segcreate ARG65_0 ARG65_1 ARG65_2 -seglinkedit -segprot ARG67_0 ARG67_1 ARG67_2 -segs_read_FOO -segs_read_only_addr ARG69 -segs_read_write_addr ARG70 -shared-libgcc -single_module -static -static-libgcc -sub_library ARG77 -sub_umbrella ARG78 -t -twolevel_namespace -twolevel_namespace_hints -u ARG82 -umbrella ARG83 -undefined ARG84 -unexported_symbols_list ARG85 -w -weak_reference_mismatches ARG87 -whatsloaded -whyload -y -filelist FOO -l FOO 2> %t.log // RUN: FileCheck -check-prefix=SPLATTER %s < %t.log -// SPLATTER: {{".*ld.*" "-static" "-dylib" "-dylib_compatibility_version" "ARG11" "-dylib_current_version" "ARG12" "-arch" "i386" "-dylib_install_name" "ARG31" "-all_load" "-allowable_client" "ARG8" "-bind_at_load" "-dead_strip" "-no_dead_strip_inits_and_terms" "-dylib_file" "ARG14" "-dynamic" "-exported_symbols_list" "ARG20" "-flat_namespace" "-headerpad_max_install_names" "-image_base" "ARG29" "-init" "ARG30" "-macosx_version_min" "10.3.2" "-nomultidefs" "-multi_module" "-single_module" "-multiply_defined" "ARG37" "-multiply_defined_unused" "ARG38" "-pie" "-prebind" "-noprebind" "-nofixprebinding" "-prebind_all_twolevel_modules" "-read_only_relocs" "ARG55" "-sectcreate" "ARG58_0" "ARG58_1" "ARG58_2" "-sectorder" "ARG60_0" "ARG60_1" "ARG60_2" "-seg1addr" "ARG61" "-segprot" "ARG67_0" "ARG67_1" "ARG67_2" "-segaddr" "ARG64_0" "ARG64_1" "-segs_read_only_addr" "ARG69" "-segs_read_write_addr" "ARG70" "-seg_addr_table" "ARG62" "-seg_addr_table_filename" "ARG63" "-sub_library" "ARG77" "-sub_umbrella" "ARG78" "-twolevel_namespace" "-twolevel_namespace_hints" "-umbrella" "ARG83" "-undefined" "ARG84" "-unexported_symbols_list" "ARG85" "-weak_reference_mismatches" "ARG87" "-X" "-y" "-w" "-pagezero_size" "ARG54" "-segs_read_FOO" "-seglinkedit" "-noseglinkedit" "-sectalign" "ARG57_0" "ARG57_1" "ARG57_2" "-sectobjectsymbols" "ARG59_0" "ARG59_1" "-segcreate" "ARG65_0" "ARG65_1" "ARG65_2" "-whyload" "-whatsloaded" "-dylinker_install_name" "ARG16" "-dylinker" "-Mach" "-d" "-s" "-t" "-Z" "-u" "ARG82" "-undefined" "ARG84" "-A" "ARG0" "-e" "ARG19" "-m" "ARG33" "-r" "-o" "a.out" "-LARG2" "-lgomp".* "-filelist" "FOO" "-lFOO" "-allow_stack_execute" ".*/libprofile_rt.a" "-T" "ARG4" "-FARG1"}} +// SPLATTER: {{".*ld.*" "-static" "-dylib" "-dylib_compatibility_version" "ARG11" "-dylib_current_version" "ARG12" "-arch" "i386" "-dylib_install_name" "ARG31" "-all_load" "-allowable_client" "ARG8" "-bind_at_load" "-dead_strip" "-no_dead_strip_inits_and_terms" "-dylib_file" "ARG14" "-dynamic" "-exported_symbols_list" "ARG20" "-flat_namespace" "-force_load" "ARG28" "-headerpad_max_install_names" "-image_base" "ARG29" "-init" "ARG30" "-macosx_version_min" "10.3.2" "-nomultidefs" "-multi_module" "-single_module" "-multiply_defined" "ARG37" "-multiply_defined_unused" "ARG38" "-pie" "-prebind" "-noprebind" "-nofixprebinding" "-prebind_all_twolevel_modules" "-read_only_relocs" "ARG55" "-sectcreate" "ARG58_0" "ARG58_1" "ARG58_2" "-sectorder" "ARG60_0" "ARG60_1" "ARG60_2" "-seg1addr" "ARG61" "-segprot" "ARG67_0" "ARG67_1" "ARG67_2" "-segaddr" "ARG64_0" "ARG64_1" "-segs_read_only_addr" "ARG69" "-segs_read_write_addr" "ARG70" "-seg_addr_table" "ARG62" "-seg_addr_table_filename" "ARG63" "-sub_library" "ARG77" "-sub_umbrella" "ARG78" "-twolevel_namespace" "-twolevel_namespace_hints" "-umbrella" "ARG83" "-undefined" "ARG84" "-unexported_symbols_list" "ARG85" "-weak_reference_mismatches" "ARG87" "-X" "-y" "-w" "-pagezero_size" "ARG54" "-segs_read_FOO" "-seglinkedit" "-noseglinkedit" "-sectalign" "ARG57_0" "ARG57_1" "ARG57_2" "-sectobjectsymbols" "ARG59_0" "ARG59_1" "-segcreate" "ARG65_0" "ARG65_1" "ARG65_2" "-whyload" "-whatsloaded" "-dylinker_install_name" "ARG16" "-dylinker" "-Mach" "-d" "-s" "-t" "-Z" "-u" "ARG82" "-undefined" "ARG84" "-A" "ARG0" "-e" "ARG19" "-m" "ARG33" "-r" "-o" "a.out" "-LARG2" "-lgomp".* "-filelist" "FOO" "-lFOO" "-allow_stack_execute" ".*/libprofile_rt.*" "-T" "ARG4" "-FARG1"}} // Check linker changes that came with new linkedit format. // RUN: touch %t.o @@ -92,3 +92,19 @@ // LINK_OLDER_NODEMANGLE: {{ld(.exe)?"}} // LINK_OLDER_NODEMANGLE-NOT: "-demangle" // LINK_OLDER_NODEMANGLE: "-lSystem" + +// RUN: %clang -ccc-host-triple x86_64-apple-darwin10 -### %t.o \ +// RUN: -mlinker-version=117 -flto 2> %t.log +// RUN: cat %t.log +// RUN: FileCheck -check-prefix=LINK_OBJECT_LTO_PATH %s < %t.log +// +// LINK_OBJECT_LTO_PATH: {{ld(.exe)?"}} +// LINK_OBJECT_LTO_PATH: "-object_path_lto" + +// RUN: %clang -ccc-host-triple x86_64-apple-darwin10 -### %t.o \ +// RUN: -force_load a -force_load b 2> %t.log +// RUN: cat %t.log +// RUN: FileCheck -check-prefix=FORCE_LOAD %s < %t.log +// +// FORCE_LOAD: {{ld(.exe)?"}} +// FORCE_LOAD: "-force_load" "a" "-force_load" "b" diff --git a/test/Driver/darwin-objc-options.m b/test/Driver/darwin-objc-options.m index bc0b12cf55b0..50daa72333e8 100644 --- a/test/Driver/darwin-objc-options.m +++ b/test/Driver/darwin-objc-options.m @@ -15,5 +15,7 @@ // CHECK-CHECK-I386_ABI2: "-cc1" // CHECK-CHECK-I386_ABI2: -fobjc-nonfragile-abi +// CHECK-CHECK-I386_ABI2: -fobjc-exceptions +// CHECK-CHECK-I386_ABI2: -fexceptions // CHECK-CHECK-I386_ABI2-NOT: -fobjc-dispatch-method // CHECK-CHECK-I386_ABI2: darwin-objc-options diff --git a/test/Driver/darwin-xarch.c b/test/Driver/darwin-xarch.c index d16d5318f7f8..4c6689cef0fc 100644 --- a/test/Driver/darwin-xarch.c +++ b/test/Driver/darwin-xarch.c @@ -12,3 +12,9 @@ // RUN: FileCheck --check-prefix=CHECK-LINK < %t %s // // CHECK-LINK: ld{{.*}} "-arch" "i386"{{.*}} "-some-linker-arg" + +// RUN: %clang -ccc-host-triple x86_64-apple-darwin10 -### \ +// RUN: -arch armv7 -Xarch_armv7 -Wl,-some-linker-arg -filelist X 2> %t +// RUN: FileCheck --check-prefix=CHECK-ARMV7-LINK < %t %s +// +// CHECK-ARMV7-LINK: ld{{.*}} "-arch" "armv7"{{.*}} "-some-linker-arg" diff --git a/test/Driver/gnu-runtime.m b/test/Driver/gnu-runtime.m new file mode 100644 index 000000000000..5a3654c8c412 --- /dev/null +++ b/test/Driver/gnu-runtime.m @@ -0,0 +1,5 @@ +// RUN: %clang -ccc-host-triple i386-apple-darwin10 -### -fsyntax-only -fgnu-runtime %s 2>&1 | FileCheck %s +// RUN: %clang -ccc-host-triple i386-apple-darwin10 -### -x objective-c++ -fsyntax-only -fgnu-runtime %s 2>&1 | FileCheck %s +// CHECK: -fgnu-runtime +// CHECK: -fobjc-runtime-has-arc +// CHECK: -fobjc-runtime-has-weak diff --git a/test/Driver/ios-simulator-arcruntime.c b/test/Driver/ios-simulator-arcruntime.c new file mode 100644 index 000000000000..bec9f7b1fdc8 --- /dev/null +++ b/test/Driver/ios-simulator-arcruntime.c @@ -0,0 +1,6 @@ +// RUN: %clang -### -x objective-c -ccc-host-triple i386-apple-darwin10 -arch i386 -mmacosx-version-min=10.6 -D__IPHONE_OS_VERSION_MIN_REQUIRED=40201 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS1 %s +// RUN: %clang -### -x objective-c -ccc-host-triple i386-apple-darwin10 -arch i386 -mmacosx-version-min=10.6 -D__IPHONE_OS_VERSION_MIN_REQUIRED=50000 -fobjc-arc -fsyntax-only %s 2>&1 | FileCheck -check-prefix=CHECK-OPTIONS2 %s +// + +// CHECK-OPTIONS1-NOT: -fobjc-runtime-has-weak +// CHECK-OPTIONS2: -fobjc-runtime-has-weak diff --git a/test/Driver/mg.c b/test/Driver/mg.c new file mode 100644 index 000000000000..4f438142f411 --- /dev/null +++ b/test/Driver/mg.c @@ -0,0 +1,5 @@ +// RUN: %clang -M -MG -include nonexistent-preinclude.h %s > %t +// RUN: fgrep nonexistent-preinclude.h %t +// RUN: fgrep nonexistent-ppinclude.h %t + +#include "nonexistent-ppinclude.h" diff --git a/test/Driver/no-objc-arr.m b/test/Driver/no-objc-arr.m new file mode 100644 index 000000000000..15fbdc20ce8a --- /dev/null +++ b/test/Driver/no-objc-arr.m @@ -0,0 +1,8 @@ +// RUN: %clang -Werror -fobjc-arc -fsyntax-only -fno-objc-arc -verify %s +// rdar://8949617 + +void * FOO() { + id string = @"Hello World.\n"; + void *pointer = string; // No error must be issued + return pointer; +} diff --git a/test/Driver/noexecstack.c b/test/Driver/noexecstack.c new file mode 100644 index 000000000000..1f4772882654 --- /dev/null +++ b/test/Driver/noexecstack.c @@ -0,0 +1 @@ +// RUN: %clang -### %s -c -o tmp.o -triple i686-pc-linux-gnu -integrated-as -Wa,--noexecstack 2>&1 | grep "mnoexecstack" diff --git a/test/Driver/rewrite-objc.m b/test/Driver/rewrite-objc.m index 2fe2ccec6a7c..c47b52334c91 100644 --- a/test/Driver/rewrite-objc.m +++ b/test/Driver/rewrite-objc.m @@ -3,7 +3,7 @@ // TEST0: clang{{.*}}" "-cc1" // TEST0: "-rewrite-objc" // FIXME: CHECK-NOT is broken somehow, it doesn't work here. Check adjacency instead. -// TEST0: "-fmessage-length" "0" "-fobjc-infer-related-result-type" "-fobjc-exceptions" "-fdiagnostics-show-option" +// TEST0: "-fmessage-length" "0" "-fno-objc-infer-related-result-type" "-fobjc-exceptions" "-fdiagnostics-show-option" // TEST0: rewrite-objc.m" // RUN: not %clang -ccc-no-clang -ccc-host-triple unknown -rewrite-objc %s -o - -### 2>&1 | \ diff --git a/test/Driver/sysroot.c b/test/Driver/sysroot.c index 34f31001acc8..79db900d07e2 100644 --- a/test/Driver/sysroot.c +++ b/test/Driver/sysroot.c @@ -1,18 +1,18 @@ // Check that --sysroot= also applies to header search paths. // RUN: %clang -ccc-host-triple i386-unk-unk --sysroot=/FOO -### -E %s 2> %t1 // RUN: FileCheck --check-prefix=CHECK-SYSROOTEQ < %t1 %s -// CHECK-SYSROOTEQ: "-cc1"{{.*}} "-isysroot" "/FOO" +// CHECK-SYSROOTEQ: "-cc1"{{.*}} "-isysroot" "{{[^"]*}}/FOO" // Apple Darwin uses -isysroot as the syslib root, too. // RUN: touch %t2.o // RUN: %clang -ccc-host-triple i386-apple-darwin10 \ // RUN: -isysroot /FOO -### %t2.o 2> %t2 // RUN: FileCheck --check-prefix=CHECK-APPLE-ISYSROOT < %t2 %s -// CHECK-APPLE-ISYSROOT: "-arch" "i386"{{.*}} "-syslibroot" "/FOO" +// CHECK-APPLE-ISYSROOT: "-arch" "i386"{{.*}} "-syslibroot" "{{[^"]*}}/FOO" // Check that honor --sysroot= over -isysroot, for Apple Darwin. // RUN: touch %t3.o // RUN: %clang -ccc-host-triple i386-apple-darwin10 \ // RUN: -isysroot /FOO --sysroot=/BAR -### %t3.o 2> %t3 // RUN: FileCheck --check-prefix=CHECK-APPLE-SYSROOT < %t3 %s -// CHECK-APPLE-SYSROOT: "-arch" "i386"{{.*}} "-syslibroot" "/BAR" +// CHECK-APPLE-SYSROOT: "-arch" "i386"{{.*}} "-syslibroot" "{{[^"]*}}/BAR" diff --git a/test/Driver/x86_features.c b/test/Driver/x86_features.c index 9dbdd0ae90b4..0db372ec64f1 100644 --- a/test/Driver/x86_features.c +++ b/test/Driver/x86_features.c @@ -1,3 +1,3 @@ // RUN: %clang -ccc-host-triple i386-unknown-unknown -### -S %s -msse -msse4 -mno-sse -mno-mmx -msse 2> %t -// RUN: grep '"-target-feature" "+sse" "-target-feature" "+sse4" "-target-feature" "-sse" "-target-feature" "-mmx" "-target-feature" "+sse"' %t - +// RUN: grep '"pentium4" "-target-feature" "+sse4" "-target-feature" "-mmx" "-target-feature" "+sse"' %t +// Note that we filter out all but the last -m(no)sse. diff --git a/test/FixIt/fixit-objc.m b/test/FixIt/fixit-objc.m index bf704c66a027..2e8bfaea6aa3 100644 --- a/test/FixIt/fixit-objc.m +++ b/test/FixIt/fixit-objc.m @@ -39,3 +39,17 @@ void f(Test *t) { [t test:@"Foo"]]; // expected-error{{extraneous ']' before ';'}} g(@"Foo")); // expected-error{{extraneous ')' before ';'}} } + +// rdar://7861841 +@interface Radar7861841 { +@public + int x; +} + +@property (assign) int y; +@end + +int f0(Radar7861841 *a) { return a.x; } // expected-error {{property 'x' not found on object of type 'Radar7861841 *'; did you mean to access ivar 'x'}} + +int f1(Radar7861841 *a) { return a->y; } // expected-error {{property 'y' found on object of type 'Radar7861841 *'; did you mean to access it with the "." operator?}} + diff --git a/test/FixIt/fixit.c b/test/FixIt/fixit.c index 1a6ef635570a..ba45cf28e694 100644 --- a/test/FixIt/fixit.c +++ b/test/FixIt/fixit.c @@ -33,9 +33,14 @@ void f1(x, y) int i0 = { 17 }; +#define ONE 1 +#define TWO 2 + int test_cond(int y, int fooBar) { // CHECK: int x = y ? 1 : 4+fooBar; int x = y ? 1 4+foobar; +// CHECK: x = y ? ONE : TWO; + x = y ? ONE TWO; return x; } diff --git a/test/FixIt/typo-crash.cpp b/test/FixIt/typo-crash.cpp new file mode 100644 index 000000000000..b156e1b44c45 --- /dev/null +++ b/test/FixIt/typo-crash.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// FIXME: The diagnostics and recovery here are very, very poor. + +// PR10355 +template<typename T> void template_id1() { + template_id2<> t; // expected-error 2{{use of undeclared identifier 'template_id2'; did you mean 'template_id1'?}} \ + // expected-error{{expected expression}} \ + // expected-error{{use of undeclared identifier 't'}} + } + diff --git a/test/FixIt/typo.cpp b/test/FixIt/typo.cpp index f8b5352374f3..3d40da8d256a 100644 --- a/test/FixIt/typo.cpp +++ b/test/FixIt/typo.cpp @@ -74,3 +74,15 @@ int foo() { unsinged *ptr = 0; // expected-error{{use of undeclared identifier 'unsinged'; did you mean 'unsigned'?}} return *i + *ptr + global_val; // expected-error{{use of undeclared identifier 'global_val'; did you mean 'global_value'?}} } + +namespace nonstd { + typedef std::basic_string<char> yarn; // expected-note{{'nonstd::yarn' declared here}} +} + +yarn str4; // expected-error{{unknown type name 'yarn'; did you mean 'nonstd::yarn'?}} + +namespace check_bool { + void f() { + Bool b; // expected-error{{use of undeclared identifier 'Bool'; did you mean 'bool'?}} + } +} diff --git a/test/Frontend/dependency-gen.c b/test/Frontend/dependency-gen.c index 0f8adabc85aa..49d3f28614c5 100644 --- a/test/Frontend/dependency-gen.c +++ b/test/Frontend/dependency-gen.c @@ -1,19 +1,27 @@ -// rdar://6533411 -// RUN: %clang -MD -MF %t.d -S -x c -o %t.o %s -// RUN: grep '.*dependency-gen.*:' %t.d -// RUN: grep 'dependency-gen.c' %t.d - -// RUN: %clang -S -M -x c %s -o %t.d -// RUN: grep '.*dependency-gen.*:' %t.d -// RUN: grep 'dependency-gen.c' %t.d - -// PR8974 // REQUIRES: shell -// "cd %t.dir" requires shell. +// Basic test // RUN: rm -rf %t.dir // RUN: mkdir -p %t.dir/a/b // RUN: echo > %t.dir/a/b/x.h // RUN: cd %t.dir -// RUN: %clang -include a/b/x.h -MD -MF %t.d -S -x c -o %t.o %s -// RUN: grep ' a/b/x\.h' %t.d +// RUN: %clang -MD -MF - %s -fsyntax-only -I a/b | FileCheck -check-prefix=CHECK-ONE %s +// CHECK-ONE: {{ }}a/b/x.h + +// PR8974 (-include flag) +// RUN: %clang -MD -MF - %s -fsyntax-only -include a/b/x.h -DINCLUDE_FLAG_TEST | FileCheck -check-prefix=CHECK-TWO %s +// CHECK-TWO: {{ }}a/b/x.h + +// rdar://problem/9734352 (paths involving ".") +// RUN: %clang -MD -MF - %s -fsyntax-only -I ./a/b | FileCheck -check-prefix=CHECK-THREE %s +// CHECK-THREE: {{ }}a/b/x.h +// RUN: %clang -MD -MF - %s -fsyntax-only -I .//./a/b/ | FileCheck -check-prefix=CHECK-FOUR %s +// CHECK-FOUR: {{ }}a/b/x.h +// RUN: %clang -MD -MF - %s -fsyntax-only -I a/b/. | FileCheck -check-prefix=CHECK-FIVE %s +// CHECK-FIVE: {{ }}a/b/./x.h +// RUN: cd a/b +// RUN: %clang -MD -MF - %s -fsyntax-only -I ./ | FileCheck -check-prefix=CHECK-SIX %s +// CHECK-SIX: {{ }}x.h +#ifndef INCLUDE_FLAG_TEST +#include <x.h> +#endif diff --git a/test/Index/annotate-tokens-pp.c b/test/Index/annotate-tokens-pp.c index 01a615f0e4cb..e6bb0869d0b1 100644 --- a/test/Index/annotate-tokens-pp.c +++ b/test/Index/annotate-tokens-pp.c @@ -53,14 +53,14 @@ void test() { // CHECK: Identifier: "X" [4:22 - 4:23] macro definition=WIBBLE // CHECK: Punctuation: "##" [4:23 - 4:25] macro definition=WIBBLE // CHECK: Identifier: "Y" [4:25 - 4:26] macro definition=WIBBLE -// CHECK: Identifier: "NOTHING" [5:1 - 5:8] macro instantiation=NOTHING:1:9 +// CHECK: Identifier: "NOTHING" [5:1 - 5:8] macro expansion=NOTHING:1:9 // CHECK: Punctuation: "(" [5:8 - 5:9] // CHECK: Identifier: "more" [5:9 - 5:13] // CHECK: Punctuation: "," [5:13 - 5:14] // CHECK: Identifier: "junk" [5:14 - 5:18] // CHECK: Punctuation: ")" [5:18 - 5:19] // CHECK: Keyword: "float" [5:20 - 5:25] -// CHECK: Identifier: "WIBBLE" [5:26 - 5:32] macro instantiation=WIBBLE:4:9 +// CHECK: Identifier: "WIBBLE" [5:26 - 5:32] macro expansion=WIBBLE:4:9 // CHECK: Punctuation: "(" [5:32 - 5:33] // CHECK: Keyword: "int" [5:33 - 5:36] // CHECK: Punctuation: "," [5:36 - 5:37] @@ -68,8 +68,8 @@ void test() { // CHECK: Punctuation: ")" [5:43 - 5:44] // CHECK: Punctuation: ";" [5:44 - 5:45] // CHECK: Keyword: "int" [6:1 - 6:4] -// CHECK: Identifier: "BAR" [6:5 - 6:8] macro instantiation=BAR:3:9 -// CHECK: Identifier: "STILL_NOTHING" [6:9 - 6:22] macro instantiation=STILL_NOTHING:2:9 +// CHECK: Identifier: "BAR" [6:5 - 6:8] macro expansion=BAR:3:9 +// CHECK: Identifier: "STILL_NOTHING" [6:9 - 6:22] macro expansion=STILL_NOTHING:2:9 // CHECK: Punctuation: ";" [6:22 - 6:23] // CHECK: Punctuation: "#" [7:1 - 7:2] inclusion directive=foo.h // CHECK: Identifier: "include" [7:2 - 7:9] inclusion directive=foo.h @@ -115,7 +115,7 @@ void test() { // CHECK: Keyword: "int" [16:3 - 16:6] VarDecl=k:16:7 (Definition) // CHECK: Identifier: "k" [16:7 - 16:8] VarDecl=k:16:7 (Definition) // CHECK: Punctuation: "=" [16:9 - 16:10] VarDecl=k:16:7 (Definition) -// CHECK: Identifier: "REVERSE_MACRO" [16:11 - 16:24] macro instantiation=REVERSE_MACRO:10:9 +// CHECK: Identifier: "REVERSE_MACRO" [16:11 - 16:24] macro expansion=REVERSE_MACRO:10:9 // CHECK: Punctuation: "(" [16:24 - 16:25] UnexposedStmt= // CHECK: Identifier: "t" [16:25 - 16:26] DeclRefExpr=t:15:7 // CHECK: Punctuation: "," [16:26 - 16:27] UnexposedStmt= @@ -125,7 +125,7 @@ void test() { // CHECK: Keyword: "int" [17:3 - 17:6] VarDecl=j:17:7 (Definition) // CHECK: Identifier: "j" [17:7 - 17:8] VarDecl=j:17:7 (Definition) // CHECK: Punctuation: "=" [17:9 - 17:10] VarDecl=j:17:7 (Definition) -// CHECK: Identifier: "TWICE_MACRO" [17:11 - 17:22] macro instantiation=TWICE_MACRO:11:9 +// CHECK: Identifier: "TWICE_MACRO" [17:11 - 17:22] macro expansion=TWICE_MACRO:11:9 // CHECK: Punctuation: "(" [17:22 - 17:23] UnexposedStmt= // CHECK: Identifier: "k" [17:23 - 17:24] DeclRefExpr=k:16:7 // CHECK: Punctuation: "+" [17:25 - 17:26] UnexposedStmt= @@ -170,7 +170,7 @@ void test() { // CHECK: Punctuation: "=" [24:9 - 24:10] VarDecl=x:24:7 (Definition) // CHECK: Literal: "10" [24:11 - 24:13] UnexposedExpr= // CHECK: Punctuation: ";" [24:13 - 24:14] UnexposedStmt= -// CHECK: Identifier: "fun_with_macro_bodies" [25:3 - 25:24] macro instantiation=fun_with_macro_bodies:21:9 +// CHECK: Identifier: "fun_with_macro_bodies" [25:3 - 25:24] macro expansion=fun_with_macro_bodies:21:9 // CHECK: Punctuation: "(" [25:24 - 25:25] UnexposedStmt= // CHECK: Identifier: "x" [25:25 - 25:26] DeclRefExpr=x:24:7 // CHECK: Punctuation: "," [25:26 - 25:27] UnexposedStmt= diff --git a/test/Index/annotate-tokens.cpp b/test/Index/annotate-tokens.cpp index ccc9e9602843..165420ab39ce 100644 --- a/test/Index/annotate-tokens.cpp +++ b/test/Index/annotate-tokens.cpp @@ -17,9 +17,10 @@ struct S1 { void f(); }; struct S2 { S1 *operator->(); }; void test3(S2 s2) { s2->f(); + X foo; } -// RUN: c-index-test -test-annotate-tokens=%s:1:1:20:1 %s | FileCheck %s +// RUN: c-index-test -test-annotate-tokens=%s:1:1:21:1 %s | FileCheck %s // CHECK: Keyword: "struct" [1:1 - 1:7] StructDecl=bonk:1:8 (Definition) // CHECK: Identifier: "bonk" [1:8 - 1:12] StructDecl=bonk:1:8 (Definition) // CHECK: Punctuation: "{" [1:13 - 1:14] StructDecl=bonk:1:8 (Definition) @@ -115,4 +116,7 @@ void test3(S2 s2) { // CHECK: Punctuation: "(" [19:8 - 19:9] CallExpr=f:16:18 // CHECK: Punctuation: ")" [19:9 - 19:10] CallExpr=f:16:18 // CHECK: Punctuation: ";" [19:10 - 19:11] UnexposedStmt= -// CHECK: Punctuation: "}" [20:1 - 20:2] UnexposedStmt= +// CHECK: Identifier: "X" [20:3 - 20:4] TypeRef=struct X:7:8 +// CHECK: Identifier: "foo" [20:5 - 20:8] VarDecl=foo:20:5 (Definition) +// CHECK: Punctuation: ";" [20:8 - 20:9] UnexposedStmt= +// CHECK: Punctuation: "}" [21:1 - 21:2] UnexposedStmt= diff --git a/test/Index/annotate-tokens.m b/test/Index/annotate-tokens.m index 1eb77940da2e..1dc762123207 100644 --- a/test/Index/annotate-tokens.m +++ b/test/Index/annotate-tokens.m @@ -134,6 +134,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; @interface Rdar8062781 + (Foo*)getB; @property (readonly, nonatomic) Foo *blah; +@property (readonly, atomic) Foo *abah; @end // RUN: c-index-test -test-annotate-tokens=%s:1:1:118:1 %s -DIBOutlet='__attribute__((iboutlet))' -DIBAction='void)__attribute__((ibaction)' | FileCheck %s @@ -226,7 +227,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Identifier: "IBActionTests" [32:12 - 32:25] ObjCInterfaceDecl=IBActionTests:32:12 // CHECK: Punctuation: "-" [33:1 - 33:2] ObjCInstanceMethodDecl=actionMethod::33:1 // CHECK: Punctuation: "(" [33:3 - 33:4] ObjCInstanceMethodDecl=actionMethod::33:1 -// CHECK: Identifier: "IBAction" [33:4 - 33:12] macro instantiation=IBAction +// CHECK: Identifier: "IBAction" [33:4 - 33:12] macro expansion=IBAction // CHECK: Punctuation: ")" [33:12 - 33:13] ObjCInstanceMethodDecl=actionMethod::33:1 // CHECK: Identifier: "actionMethod" [33:14 - 33:26] ObjCInstanceMethodDecl=actionMethod::33:1 // CHECK: Punctuation: ":" [33:26 - 33:27] ObjCInstanceMethodDecl=actionMethod::33:1 @@ -261,7 +262,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Identifier: "IBActionTests" [37:17 - 37:30] ObjCImplementationDecl=IBActionTests:37:1 (Definition) // CHECK: Punctuation: "-" [38:1 - 38:2] ObjCInstanceMethodDecl=actionMethod::38:1 (Definition) // CHECK: Punctuation: "(" [38:3 - 38:4] ObjCInstanceMethodDecl=actionMethod::38:1 (Definition) -// CHECK: Identifier: "IBAction" [38:4 - 38:12] macro instantiation=IBAction +// CHECK: Identifier: "IBAction" [38:4 - 38:12] macro expansion=IBAction // CHECK: Punctuation: ")" [38:12 - 38:13] ObjCInstanceMethodDecl=actionMethod::38:1 (Definition) // CHECK: Identifier: "actionMethod" [38:14 - 38:26] ObjCInstanceMethodDecl=actionMethod::38:1 (Definition) // CHECK: Punctuation: ":" [38:26 - 38:27] ObjCInstanceMethodDecl=actionMethod::38:1 (Definition) @@ -305,7 +306,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Keyword: "interface" [51:2 - 51:11] ObjCInterfaceDecl=IBOutletTests:51:12 // CHECK: Identifier: "IBOutletTests" [51:12 - 51:25] ObjCInterfaceDecl=IBOutletTests:51:12 // CHECK: Punctuation: "{" [52:1 - 52:2] ObjCInterfaceDecl=IBOutletTests:51:12 -// CHECK: Identifier: "IBOutlet" [53:5 - 53:13] macro instantiation=IBOutlet +// CHECK: Identifier: "IBOutlet" [53:5 - 53:13] macro expansion=IBOutlet // CHECK: Keyword: "char" [53:14 - 53:18] ObjCIvarDecl=anOutlet:53:21 (Definition) // CHECK: Punctuation: "*" [53:19 - 53:20] ObjCIvarDecl=anOutlet:53:21 (Definition) // CHECK: Identifier: "anOutlet" [53:21 - 53:29] ObjCIvarDecl=anOutlet:53:21 (Definition) @@ -313,7 +314,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Punctuation: "}" [54:1 - 54:2] ObjCInterfaceDecl=IBOutletTests:51:12 // CHECK: Punctuation: "-" [55:1 - 55:2] ObjCInstanceMethodDecl=actionMethod::55:1 // CHECK: Punctuation: "(" [55:3 - 55:4] ObjCInstanceMethodDecl=actionMethod::55:1 -// CHECK: Identifier: "IBAction" [55:4 - 55:12] macro instantiation=IBAction +// CHECK: Identifier: "IBAction" [55:4 - 55:12] macro expansion=IBAction // CHECK: Punctuation: ")" [55:12 - 55:13] ObjCInstanceMethodDecl=actionMethod::55:1 // CHECK: Identifier: "actionMethod" [55:14 - 55:26] ObjCInstanceMethodDecl=actionMethod::55:1 // CHECK: Punctuation: ":" [55:26 - 55:27] ObjCInstanceMethodDecl=actionMethod::55:1 @@ -324,7 +325,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Punctuation: ";" [55:34 - 55:35] ObjCInstanceMethodDecl=actionMethod::55:1 // CHECK: Punctuation: "@" [56:1 - 56:2] ObjCPropertyDecl=aPropOutlet:56:26 // CHECK: Keyword: "property" [56:2 - 56:10] ObjCPropertyDecl=aPropOutlet:56:26 -// CHECK: Identifier: "IBOutlet" [56:11 - 56:19] macro instantiation=IBOutlet +// CHECK: Identifier: "IBOutlet" [56:11 - 56:19] macro expansion=IBOutlet // CHECK: Keyword: "int" [56:20 - 56:23] ObjCPropertyDecl=aPropOutlet:56:26 // CHECK: Punctuation: "*" [56:24 - 56:25] ObjCPropertyDecl=aPropOutlet:56:26 // CHECK: Identifier: "aPropOutlet" [56:26 - 56:37] ObjCPropertyDecl=aPropOutlet:56:26 @@ -388,7 +389,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK: Identifier: "self" [76:18 - 76:22] DeclRefExpr=self:0:0 // CHECK: Identifier: "foo" [76:23 - 76:26] ObjCMessageExpr=foo::66:1 // CHECK: Punctuation: ":" [76:26 - 76:27] ObjCMessageExpr=foo::66:1 -// CHECK: Identifier: "VAL" [76:27 - 76:30] macro instantiation=VAL:63:9 +// CHECK: Identifier: "VAL" [76:27 - 76:30] macro expansion=VAL:63:9 // CHECK: Punctuation: "]" [76:30 - 76:31] ObjCMessageExpr=foo::66:1 // CHECK: Punctuation: ";" [76:31 - 76:32] UnexposedStmt= // CHECK: Keyword: "int" [77:5 - 77:8] VarDecl=second:77:9 (Definition) @@ -526,7 +527,7 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK-INSIDE_BLOCK: Punctuation: "=" [128:20 - 128:21] VarDecl=a:128:18 (Definition) // CHECK-INSIDE_BLOCK: Identifier: "self" [128:22 - 128:26] DeclRefExpr=self:0:0 -// RUN: c-index-test -test-annotate-tokens=%s:134:1:137:1 %s -DIBOutlet='__attribute__((iboutlet))' -DIBAction='void)__attribute__((ibaction)' | FileCheck -check-prefix=CHECK-PROP-AFTER-METHOD %s +// RUN: c-index-test -test-annotate-tokens=%s:134:1:138:1 %s -DIBOutlet='__attribute__((iboutlet))' -DIBAction='void)__attribute__((ibaction)' | FileCheck -check-prefix=CHECK-PROP-AFTER-METHOD %s // CHECK-PROP-AFTER-METHOD: Punctuation: "@" [134:1 - 134:2] ObjCInterfaceDecl=Rdar8062781:134:12 // CHECK-PROP-AFTER-METHOD: Keyword: "interface" [134:2 - 134:11] ObjCInterfaceDecl=Rdar8062781:134:12 // CHECK-PROP-AFTER-METHOD: Identifier: "Rdar8062781" [134:12 - 134:23] ObjCInterfaceDecl=Rdar8062781:134:12 @@ -548,4 +549,15 @@ static Rdar8595462_A * Rdar8595462_staticVar; // CHECK-PROP-AFTER-METHOD: Punctuation: "*" [136:37 - 136:38] ObjCPropertyDecl=blah:136:38 // CHECK-PROP-AFTER-METHOD: Identifier: "blah" [136:38 - 136:42] ObjCPropertyDecl=blah:136:38 // CHECK-PROP-AFTER-METHOD: Punctuation: ";" [136:42 - 136:43] ObjCInterfaceDecl=Rdar8062781:134:12 -// CHECK-PROP-AFTER-METHOD: Punctuation: "@" [137:1 - 137:2] ObjCInterfaceDecl=Rdar8062781:134:12 +// CHECK-PROP-AFTER-METHOD: Punctuation: "@" [137:1 - 137:2] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Keyword: "property" [137:2 - 137:10] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Punctuation: "(" [137:11 - 137:12] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Keyword: "readonly" [137:12 - 137:20] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Punctuation: "," [137:20 - 137:21] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Keyword: "atomic" [137:22 - 137:28] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Punctuation: ")" [137:28 - 137:29] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Identifier: "Foo" [137:30 - 137:33] ObjCClassRef=Foo:1:12 +// CHECK-PROP-AFTER-METHOD: Punctuation: "*" [137:34 - 137:35] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Identifier: "abah" [137:35 - 137:39] ObjCPropertyDecl=abah:137:35 +// CHECK-PROP-AFTER-METHOD: Punctuation: ";" [137:39 - 137:40] ObjCInterfaceDecl=Rdar8062781:134:12 +// CHECK-PROP-AFTER-METHOD: Punctuation: "@" [138:1 - 138:2] ObjCInterfaceDecl=Rdar8062781:134:12 diff --git a/test/Index/arc-annotate.m b/test/Index/arc-annotate.m new file mode 100644 index 000000000000..b836bc8b26de --- /dev/null +++ b/test/Index/arc-annotate.m @@ -0,0 +1,38 @@ +@interface A +@property (strong, nonatomic) id property; +@property (nonatomic, weak) id second_property; +@property (unsafe_unretained, nonatomic) id third_property; +@end + +// RUN: c-index-test -test-annotate-tokens=%s:1:1:5:1 %s -fobjc-arc -fobjc-nonfragile-abi | FileCheck %s +// CHECK: Punctuation: "@" [1:1 - 1:2] ObjCInterfaceDecl=A:1:12 +// CHECK: Keyword: "interface" [1:2 - 1:11] ObjCInterfaceDecl=A:1:12 +// CHECK: Identifier: "A" [1:12 - 1:13] ObjCInterfaceDecl=A:1:12 +// CHECK: Punctuation: "@" [2:1 - 2:2] ObjCPropertyDecl=property:2:34 +// CHECK: Keyword: "property" [2:2 - 2:10] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: "(" [2:11 - 2:12] ObjCPropertyDecl=property:2:34 +// CHECK: Keyword: "strong" [2:12 - 2:18] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: "," [2:18 - 2:19] ObjCPropertyDecl=property:2:34 +// CHECK: Keyword: "nonatomic" [2:20 - 2:29] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: ")" [2:29 - 2:30] ObjCPropertyDecl=property:2:34 +// CHECK: Identifier: "id" [2:31 - 2:33] TypeRef=id:0:0 +// CHECK: Identifier: "property" [2:34 - 2:42] ObjCPropertyDecl=property:2:34 +// CHECK: Punctuation: ";" [2:42 - 2:43] ObjCInterfaceDecl=A:1:12 +// CHECK: Punctuation: "@" [3:1 - 3:2] ObjCPropertyDecl=second_property:3:32 +// CHECK: Keyword: "property" [3:2 - 3:10] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: "(" [3:11 - 3:12] ObjCPropertyDecl=second_property:3:32 +// CHECK: Keyword: "nonatomic" [3:12 - 3:21] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: "," [3:21 - 3:22] ObjCPropertyDecl=second_property:3:32 +// CHECK: Keyword: "weak" [3:23 - 3:27] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: ")" [3:27 - 3:28] ObjCPropertyDecl=second_property:3:32 +// CHECK: Identifier: "id" [3:29 - 3:31] TypeRef=id:0:0 +// CHECK: Identifier: "second_property" [3:32 - 3:47] ObjCPropertyDecl=second_property:3:32 +// CHECK: Punctuation: "@" [4:1 - 4:2] ObjCPropertyDecl=third_property:4:45 +// CHECK: Keyword: "property" [4:2 - 4:10] ObjCPropertyDecl=third_property:4:45 +// CHECK: Punctuation: "(" [4:11 - 4:12] ObjCPropertyDecl=third_property:4:45 +// CHECK: Keyword: "unsafe_unretained" [4:12 - 4:29] ObjCPropertyDecl=third_property:4:45 +// CHECK: Punctuation: "," [4:29 - 4:30] ObjCPropertyDecl=third_property:4:45 +// CHECK: Keyword: "nonatomic" [4:31 - 4:40] ObjCPropertyDecl=third_property:4:45 +// CHECK: Punctuation: ")" [4:40 - 4:41] ObjCPropertyDecl=third_property:4:45 +// CHECK: Identifier: "id" [4:42 - 4:44] TypeRef=id:0:0 +// CHECK: Identifier: "third_property" [4:45 - 4:59] ObjCPropertyDecl=third_property:4:45 diff --git a/test/Index/arc-complete.m b/test/Index/arc-complete.m new file mode 100644 index 000000000000..328983c441ec --- /dev/null +++ b/test/Index/arc-complete.m @@ -0,0 +1,16 @@ +typedef const void *CFTypeRef; + +void test(id x) { + (__bridge CFTypeRef)x; +} + + + +// RUN: c-index-test -code-completion-at=%s:4:4 %s -fobjc-arc -fobjc-nonfragile-abi | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: macro definition:{TypedText __autoreleasing} (70) +// CHECK-CC1: NotImplemented:{TypedText __bridge}{HorizontalSpace }{Placeholder type}{RightParen )}{Placeholder expression} (40) +// CHECK-CC1: NotImplemented:{TypedText __bridge_retained}{HorizontalSpace }{Placeholder CF type}{RightParen )}{Placeholder expression} (40) +// CHECK-CC1: NotImplemented:{TypedText __bridge_transfer}{HorizontalSpace }{Placeholder Objective-C type}{RightParen )}{Placeholder expression} (40) +// CHECK-CC1: macro definition:{TypedText __strong} (70) +// CHECK-CC1: macro definition:{TypedText __unsafe_unretained} (70) +// CHECK-CC1: macro definition:{TypedText __weak} (70) diff --git a/test/Index/c-index-api-loadTU-test.m b/test/Index/c-index-api-loadTU-test.m index 98a41d837fdc..ab5f9a9ed6f9 100644 --- a/test/Index/c-index-api-loadTU-test.m +++ b/test/Index/c-index-api-loadTU-test.m @@ -70,6 +70,11 @@ typedef struct X0 X1; struct X0; struct X0 {}; +@interface TestAttributes() +// <rdar://problem/9561076> +@property (retain) IBOutlet id anotherOutlet; +@end + // CHECK: c-index-api-loadTU-test.m:4:12: ObjCInterfaceDecl=Foo:4:12 Extent=[4:1 - 12:5] // CHECK: c-index-api-loadTU-test.m:6:32: ObjCIvarDecl=myoutlet:6:32 (Definition) Extent=[6:3 - 6:40] // CHECK: <invalid loc>:0:0: attribute(iboutlet)= @@ -150,3 +155,9 @@ struct X0 {}; // CHECK: c-index-api-loadTU-test.m:70:8: StructDecl=X0:70:8 Extent=[70:1 - 70:10] // CHECK: c-index-api-loadTU-test.m:71:8: StructDecl=X0:71:8 (Definition) Extent=[71:1 - 71:14] +// CHECK: <invalid loc>:0:0: attribute(iboutlet)= +// CHECK: c-index-api-loadTU-test.m:75:29: TypeRef=id:0:0 Extent=[75:29 - 75:31] +// CHECK: c-index-api-loadTU-test.m:75:32: ObjCInstanceMethodDecl=anotherOutlet:75:32 Extent=[75:32 - 75:45] +// CHECK: c-index-api-loadTU-test.m:75:32: ObjCInstanceMethodDecl=setAnotherOutlet::75:32 Extent=[75:32 - 75:45] +// CHECK: c-index-api-loadTU-test.m:75:32: ParmDecl=anotherOutlet:75:32 (Definition) Extent=[75:32 - 75:45] + diff --git a/test/Index/c-index-getCursor-pp.c b/test/Index/c-index-getCursor-pp.c index 8f986912cd6b..df61ec0329e5 100644 --- a/test/Index/c-index-getCursor-pp.c +++ b/test/Index/c-index-getCursor-pp.c @@ -18,15 +18,15 @@ B(int x); // RUN: c-index-test -cursor-at=%s:2:14 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-2 %s // CHECK-2: macro definition=DECORATION // RUN: c-index-test -cursor-at=%s:5:7 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-3 %s -// CHECK-3: macro instantiation=OBSCURE:1:9 +// CHECK-3: macro expansion=OBSCURE:1:9 // RUN: c-index-test -cursor-at=%s:6:6 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-4 %s -// CHECK-4: macro instantiation=OBSCURE:1:9 +// CHECK-4: macro expansion=OBSCURE:1:9 // RUN: c-index-test -cursor-at=%s:6:19 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-5 %s -// CHECK-5: macro instantiation=DECORATION:2:9 +// CHECK-5: macro expansion=DECORATION:2:9 // RUN: c-index-test -cursor-at=%s:9:10 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-6 %s // CHECK-6: inclusion directive=a.h // RUN: c-index-test -cursor-at=%s:14:1 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-7 %s -// CHECK-7: macro instantiation=B:12:9 +// CHECK-7: macro expansion=B:12:9 // Same tests, but with "editing" optimizations // RUN: env CINDEXTEST_EDITING=1 c-index-test -cursor-at=%s:1:11 -I%S/Inputs %s | FileCheck -check-prefix=CHECK-1 %s diff --git a/test/Index/c-index-getCursor-test.m b/test/Index/c-index-getCursor-test.m index c2ff6963c9ff..6df8c1abfeed 100644 --- a/test/Index/c-index-getCursor-test.m +++ b/test/Index/c-index-getCursor-test.m @@ -165,4 +165,4 @@ void f() { // CHECK: [55:9 - 55:26] macro definition=CONCAT // CHECK: [57:1 - 57:10] FunctionDecl=f:57:6 (Definition) // CHECK: [58:4 - 58:8] VarDecl=my_var:58:8 (Definition) -// CHECK: [58:8 - 58:14] macro instantiation=CONCAT:55:9 +// CHECK: [58:8 - 58:14] macro expansion=CONCAT:55:9 diff --git a/test/Index/code-completion.cpp b/test/Index/code-completion.cpp index 62b9e4dd1adc..f75d61f1b04b 100644 --- a/test/Index/code-completion.cpp +++ b/test/Index/code-completion.cpp @@ -52,10 +52,21 @@ Z::operator int() const { // CHECK-MEMBER: CXXDestructor:{ResultType void}{Informative X::}{TypedText ~X}{LeftParen (}{RightParen )} // CHECK-MEMBER: CXXDestructor:{ResultType void}{Informative Y::}{TypedText ~Y}{LeftParen (}{RightParen )} // CHECK-MEMBER: CXXDestructor:{ResultType void}{TypedText ~Z}{LeftParen (}{RightParen )} +// CHECK-MEMBER: Completion contexts: +// CHECK-MEMBER-NEXT: Dot member access // CHECK-OVERLOAD: NotImplemented:{ResultType int &}{Text overloaded}{LeftParen (}{Text Z z}{Comma , }{CurrentParameter int second}{RightParen )} // CHECK-OVERLOAD: NotImplemented:{ResultType float &}{Text overloaded}{LeftParen (}{Text int i}{Comma , }{CurrentParameter long second}{RightParen )} // CHECK-OVERLOAD: NotImplemented:{ResultType double &}{Text overloaded}{LeftParen (}{Text float f}{Comma , }{CurrentParameter int second}{RightParen )} +// CHECK-OVERLOAD: Completion contexts: +// CHECK-OVERLOAD-NEXT: Any type +// CHECK-OVERLOAD-NEXT: Any value +// CHECK-OVERLOAD-NEXT: Enum tag +// CHECK-OVERLOAD-NEXT: Union tag +// CHECK-OVERLOAD-NEXT: Struct tag +// CHECK-OVERLOAD-NEXT: Class name +// CHECK-OVERLOAD-NEXT: Nested name specifier +// CHECK-OVERLOAD-NEXT: Objective-C interface // RUN: c-index-test -code-completion-at=%s:37:10 %s | FileCheck -check-prefix=CHECK-EXPR %s // CHECK-EXPR: NotImplemented:{TypedText int} (50) @@ -65,3 +76,12 @@ Z::operator int() const { // CHECK-EXPR: FieldDecl:{ResultType float}{Text Y::}{TypedText member} (18) // CHECK-EXPR: CXXMethod:{ResultType void}{TypedText memfunc}{LeftParen (}{Optional {Placeholder int i}}{RightParen )} (37) // CHECK-EXPR: Namespace:{TypedText N}{Text ::} (75) +// CHECK-EXPR: Completion contexts: +// CHECK-EXPR-NEXT: Any type +// CHECK-EXPR-NEXT: Any value +// CHECK-EXPR-NEXT: Enum tag +// CHECK-EXPR-NEXT: Union tag +// CHECK-EXPR-NEXT: Struct tag +// CHECK-EXPR-NEXT: Class name +// CHECK-EXPR-NEXT: Nested name specifier +// CHECK-EXPR-NEXT: Objective-C interface diff --git a/test/Index/complete-exprs.m b/test/Index/complete-exprs.m index 0446dcd2fbdf..dfa114dcc095 100644 --- a/test/Index/complete-exprs.m +++ b/test/Index/complete-exprs.m @@ -7,10 +7,10 @@ typedef signed char BOOL; @property int prop1; @end - +__strong id global; @implementation A - (int)method:(id)param1 { - + void foo(id (^block)(id x, A* y)); for(BOOL B = YES; ; ) { } } @end @@ -27,3 +27,8 @@ typedef signed char BOOL; // CHECK-CC2: TypedefDecl:{TypedText BOOL} (50) // CHECK-CC2: NotImplemented:{TypedText char} (50) // CHECK-CC2: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (40) +// RUN: c-index-test -code-completion-at=%s:15:1 -fobjc-arc -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC3 %s +// RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_COMPLETION_CACHING=1 c-index-test -code-completion-at=%s:15:1 -fobjc-arc -fobjc-nonfragile-abi %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: FunctionDecl:{ResultType void}{TypedText foo}{LeftParen (}{Placeholder ^id(id x, A *y)block}{RightParen )} (34) +// CHECK-CC3: VarDecl:{ResultType id}{TypedText global} (50) +// CHECK-CC3: ParmDecl:{ResultType id}{TypedText param1} (34) diff --git a/test/Index/complete-natural.m b/test/Index/complete-natural.m index e1aba393ae71..326b8a7a0cdf 100644 --- a/test/Index/complete-natural.m +++ b/test/Index/complete-natural.m @@ -11,8 +11,9 @@ char in_char = 'a'; // RUN: c-index-test -code-completion-at=%s:4:32 %s > %t // RUN: echo "DONE" >> %t // RUN: FileCheck -check-prefix=CHECK-CC1 %s < %t -// CHECK-CC1-NOT: : -// CHECK-CC1: DONE +// CHECK-CC1: Completion contexts: +// CHECK-CC1-NEXT: Natural language +// CHECK-CC1-NEXT: DONE // RUN: c-index-test -code-completion-at=%s:5:18 %s > %t // RUN: echo "DONE" >> %t // RUN: FileCheck -check-prefix=CHECK-CC1 %s < %t diff --git a/test/Index/complete-property-flags.m b/test/Index/complete-property-flags.m index af9e214a292b..f2e08c36607c 100644 --- a/test/Index/complete-property-flags.m +++ b/test/Index/complete-property-flags.m @@ -16,6 +16,8 @@ // CHECK-CC1-NEXT: {TypedText readwrite} // CHECK-CC1-NEXT: {TypedText retain} // CHECK-CC1-NEXT: {TypedText setter}{Text = }{Placeholder method} +// CHECK-CC1-NEXT: {TypedText strong} +// CHECK-CC1-NEXT: {TypedText unsafe_unretained} // RUN: c-index-test -code-completion-at=%s:8:18 %s | FileCheck -check-prefix=CHECK-CC2 %s // CHECK-CC2: {TypedText getter}{Text = }{Placeholder method} // CHECK-CC2-NEXT: {TypedText nonatomic} diff --git a/test/Index/get-cursor.cpp b/test/Index/get-cursor.cpp index f26d9825a102..2aa76c472569 100644 --- a/test/Index/get-cursor.cpp +++ b/test/Index/get-cursor.cpp @@ -31,6 +31,10 @@ struct YDerived : Y { X getAnotherX() { return member; } }; +void test() { + X foo; +} + // RUN: c-index-test -cursor-at=%s:12:20 %s | FileCheck -check-prefix=CHECK-VALUE-REF %s // RUN: c-index-test -cursor-at=%s:13:21 %s | FileCheck -check-prefix=CHECK-VALUE-REF %s // RUN: c-index-test -cursor-at=%s:13:28 %s | FileCheck -check-prefix=CHECK-VALUE-REF %s @@ -61,3 +65,6 @@ struct YDerived : Y { // RUN: c-index-test -cursor-at=%s:27:10 %s | FileCheck -check-prefix=CHECK-IMPLICIT-MEMREF %s // RUN: c-index-test -cursor-at=%s:31:28 %s | FileCheck -check-prefix=CHECK-IMPLICIT-MEMREF %s // CHECK-IMPLICIT-MEMREF: MemberRefExpr=member:21:7 + +// RUN: c-index-test -cursor-at=%s:35:5 %s | FileCheck -check-prefix=CHECK-DECL %s +// CHECK-DECL: VarDecl=foo:35:5 diff --git a/test/Index/nested-macro-instantiations.cpp b/test/Index/nested-macro-instantiations.cpp index d0c9bbb1e763..9d0c0521f404 100644 --- a/test/Index/nested-macro-instantiations.cpp +++ b/test/Index/nested-macro-instantiations.cpp @@ -7,14 +7,14 @@ WIBBLE(int x); // RUN: env CINDEXTEST_NESTED_MACROS=1 c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITH-NESTED %s // RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_NESTED_MACROS=1 c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITH-NESTED %s // RUN: env CINDEXTEST_EDITING=1 CINDEXTEST_NESTED_MACROS=1 c-index-test -test-load-source-reparse 5 all %s | FileCheck -check-prefix CHECK-WITH-NESTED %s -// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:5:1: macro instantiation=WIBBLE:3:9 Extent=[5:1 - 5:7] -// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:3:19: macro instantiation=BAR:2:9 Extent=[3:19 - 5:14] -// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:2:16: macro instantiation=FOO:1:9 Extent=[2:16 - 5:14] +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:5:1: macro expansion=WIBBLE:3:9 Extent=[5:1 - 5:7] +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:3:19: macro expansion=BAR:2:9 Extent=[3:19 - 5:14] +// CHECK-WITH-NESTED: nested-macro-instantiations.cpp:2:16: macro expansion=FOO:1:9 Extent=[2:16 - 5:14] // CHECK-WITH-NESTED: nested-macro-instantiations.cpp:5:1: VarDecl=x:5:1 (Definition) Extent=[5:1 - 5:14] // RUN: c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITHOUT-NESTED %s // RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source all %s | FileCheck -check-prefix CHECK-WITHOUT-NESTED %s // RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source-reparse 5 all %s | FileCheck -check-prefix CHECK-WITHOUT-NESTED %s -// CHECK-WITHOUT-NESTED: nested-macro-instantiations.cpp:5:1: macro instantiation=WIBBLE:3:9 Extent=[5:1 - 5:7] -// CHECK-WITHOUT-NESTED-NOT: nested-macro-instantiations.cpp:3:19: macro instantiation=BAR:2:9 Extent=[3:19 - 5:14] +// CHECK-WITHOUT-NESTED: nested-macro-instantiations.cpp:5:1: macro expansion=WIBBLE:3:9 Extent=[5:1 - 5:7] +// CHECK-WITHOUT-NESTED-NOT: nested-macro-instantiations.cpp:3:19: macro expansion=BAR:2:9 Extent=[3:19 - 5:14] // CHECK-WITHOUT-NESTED: nested-macro-instantiations.cpp:5:1: VarDecl=x:5:1 (Definition) Extent=[5:1 - 5:14] diff --git a/test/Index/werror.c b/test/Index/werror.c new file mode 100644 index 000000000000..150095d8599c --- /dev/null +++ b/test/Index/werror.c @@ -0,0 +1,15 @@ +inline int *get_int_ptr(float *fp) { + return fp; +} + +#ifdef FATAL +void fatal(int); +void fatal(float); +#endif + +// CHECK-FATAL: translation errors + +// RUN: c-index-test -write-pch %t.pch -Werror %s +// RUN: not c-index-test -write-pch %t.pch -DFATAL -Werror %s 2>%t.err +// RUN: FileCheck -check-prefix=CHECK-FATAL %s < %t.err + diff --git a/test/Lexer/has_feature_objc_arc.m b/test/Lexer/has_feature_objc_arc.m new file mode 100644 index 000000000000..cd41900c60b7 --- /dev/null +++ b/test/Lexer/has_feature_objc_arc.m @@ -0,0 +1,20 @@ +// RUN: %clang_cc1 -E %s -fobjc-nonfragile-abi -fobjc-arc "-triple" "x86_64-apple-macosx10.7.0" -fobjc-runtime-has-weak | FileCheck --check-prefix=CHECK-ARC %s +// RUN: %clang_cc1 -E %s -fobjc-nonfragile-abi -fobjc-arc "-triple" "x86_64-apple-macosx10.6.0" | FileCheck --check-prefix=CHECK-ARCLITE %s + +#if __has_feature(objc_arc) +void has_objc_arc_feature(); +#else +void no_objc_arc_feature(); +#endif + +#if __has_feature(objc_arc_weak) +void has_objc_arc_weak_feature(); +#else +void no_objc_arc_weak_feature(); +#endif + +// CHECK-ARC: void has_objc_arc_feature(); +// CHECK-ARC: void has_objc_arc_weak_feature(); + +// CHECK-ARCLITE: void has_objc_arc_feature(); +// CHECK-ARCLITE: void no_objc_arc_weak_feature(); diff --git a/test/Makefile b/test/Makefile index 050c3a51ddec..4fdafe1481b1 100644 --- a/test/Makefile +++ b/test/Makefile @@ -18,7 +18,7 @@ ifndef TESTARGS ifdef VERBOSE TESTARGS = -v else -TESTARGS = -s -v +TESTARGS = -s -v endif endif diff --git a/test/Misc/caret-diags-macros.c b/test/Misc/caret-diags-macros.c index e45ad2a60518..ac83ecc69525 100644 --- a/test/Misc/caret-diags-macros.c +++ b/test/Misc/caret-diags-macros.c @@ -5,9 +5,9 @@ void foo() { M1( M2); - // CHECK: {{.*}}:6:{{[0-9]+}}: warning: expression result unused - // CHECK: {{.*}}:7:{{[0-9]+}}: note: instantiated from: - // CHECK: {{.*}}:4:{{[0-9]+}}: note: instantiated from: + // CHECK: :7:{{[0-9]+}}: warning: expression result unused + // CHECK: :4:{{[0-9]+}}: note: expanded from: + // CHECK: :3:{{[0-9]+}}: note: expanded from: } #define A 1 @@ -15,13 +15,12 @@ void foo() { #define C B void bar() { C; - // CHECK: {{.*}}:17:{{[0-9]+}}: warning: expression result unused - // CHECK: {{.*}}:15:{{[0-9]+}}: note: instantiated from: - // CHECK: {{.*}}:14:{{[0-9]+}}: note: instantiated from: - // CHECK: {{.*}}:13:{{[0-9]+}}: note: instantiated from: + // CHECK: :17:3: warning: expression result unused + // CHECK: :15:11: note: expanded from: + // CHECK: :14:11: note: expanded from: + // CHECK: :13:11: note: expanded from: } - // rdar://7597492 #define sprintf(str, A, B) \ __builtin___sprintf_chk (str, 0, 42, A, B) @@ -30,3 +29,95 @@ void baz(char *Msg) { sprintf(Msg, " sizeof FoooLib : =%3u\n", 12LL); } + +// PR9279: comprehensive tests for multi-level macro back traces +#define macro_args1(x) x +#define macro_args2(x) macro_args1(x) +#define macro_args3(x) macro_args2(x) + +#define macro_many_args1(x, y, z) y +#define macro_many_args2(x, y, z) macro_many_args1(x, y, z) +#define macro_many_args3(x, y, z) macro_many_args2(x, y, z) + +void test() { + macro_args3(1); + // CHECK: {{.*}}:43:15: warning: expression result unused + // Also check that the 'caret' printing agrees with the location here where + // its easy to FileCheck. + // CHECK-NEXT: macro_args3(1); + // CHECK-NEXT: ~~~~~~~~~~~~^~ + // CHECK: {{.*}}:36:36: note: expanded from: + // CHECK: {{.*}}:35:36: note: expanded from: + // CHECK: {{.*}}:34:24: note: expanded from: + + macro_many_args3( + 1, + 2, + 3); + // CHECK: {{.*}}:55:5: warning: expression result unused + // CHECK: {{.*}}:40:55: note: expanded from: + // CHECK: {{.*}}:39:55: note: expanded from: + // CHECK: {{.*}}:38:35: note: expanded from: + + macro_many_args3( + 1, + M2, + 3); + // CHECK: {{.*}}:64:5: warning: expression result unused + // CHECK: {{.*}}:4:12: note: expanded from: + // CHECK: {{.*}}:40:55: note: expanded from: + // CHECK: {{.*}}:39:55: note: expanded from: + // CHECK: {{.*}}:38:35: note: expanded from: + + macro_many_args3( + 1, + macro_args2(2), + 3); + // CHECK: {{.*}}:74:17: warning: expression result unused + // This caret location needs to be printed *inside* a different macro's + // arguments. + // CHECK-NEXT: macro_args2(2), + // CHECK-NEXT: ~~~~~~~~~~~~^~~ + // CHECK: {{.*}}:35:36: note: expanded from: + // CHECK: {{.*}}:34:24: note: expanded from: + // CHECK: {{.*}}:40:55: note: expanded from: + // CHECK: {{.*}}:39:55: note: expanded from: + // CHECK: {{.*}}:38:35: note: expanded from: +} + +#define variadic_args1(x, y, ...) y +#define variadic_args2(x, ...) variadic_args1(x, __VA_ARGS__) +#define variadic_args3(x, y, ...) variadic_args2(x, y, __VA_ARGS__) + +void test2() { + variadic_args3(1, 2, 3, 4); + // CHECK: {{.*}}:93:21: warning: expression result unused + // CHECK-NEXT: variadic_args3(1, 2, 3, 4); + // CHECK-NEXT: ~~~~~~~~~~~~~~~~~~^~~~~~~~ + // CHECK: {{.*}}:90:53: note: expanded from: + // CHECK: {{.*}}:89:50: note: expanded from: + // CHECK: {{.*}}:88:35: note: expanded from: +} + +#define variadic_pasting_args1(x, y, z) y +#define variadic_pasting_args2(x, ...) variadic_pasting_args1(x ## __VA_ARGS__) +#define variadic_pasting_args2a(x, y, ...) variadic_pasting_args1(x, y ## __VA_ARGS__) +#define variadic_pasting_args3(x, y, ...) variadic_pasting_args2(x, y, __VA_ARGS__) +#define variadic_pasting_args3a(x, y, ...) variadic_pasting_args2a(x, y, __VA_ARGS__) + +void test3() { + variadic_pasting_args3(1, 2, 3, 4); + // CHECK: {{.*}}:109:32: warning: expression result unused + // CHECK: {{.*}}:105:72: note: expanded from: + // CHECK: {{.*}}:103:68: note: expanded from: + // CHECK: {{.*}}:102:41: note: expanded from: + + variadic_pasting_args3a(1, 2, 3, 4); + // FIXME: It'd be really nice to retain the start location of the first token + // involved in the token paste instead of falling back on the full macro + // location in the first two locations here. + // CHECK: {{.*}}:115:3: warning: expression result unused + // CHECK: {{.*}}:106:44: note: expanded from: + // CHECK: {{.*}}:104:72: note: expanded from: + // CHECK: {{.*}}:102:41: note: expanded from: +} diff --git a/test/Misc/diag-aka-types.cpp b/test/Misc/diag-aka-types.cpp index e0e6b8c7c7af..042c70b18a75 100644 --- a/test/Misc/diag-aka-types.cpp +++ b/test/Misc/diag-aka-types.cpp @@ -12,3 +12,41 @@ char c2 = ref; // expected-error{{'const foo_t' (aka 'const X')}} // deduced auto should not produce an aka. auto aut = X(); char c3 = aut; // expected-error{{from 'X' to 'char'}} + +// There are two classes named Foo::foo here. Make sure the message gives +// a way to them apart. +namespace Foo { + class foo {}; +} + +namespace bar { + namespace Foo { + class foo; + } + void f(Foo::foo* x); // expected-note{{passing argument to parameter 'x' here}} +} + +void test(Foo::foo* x) { + bar::f(x); // expected-error{{cannot initialize a parameter of type 'Foo::foo *' (aka 'bar::Foo::foo *') with an lvalue of type 'Foo::foo *')}} +} + +// PR9548 - "no known conversion from 'vector<string>' to 'vector<string>'" +// vector<string> refers to two different types here. Make sure the message +// gives a way to tell them apart. +class versa_string; +typedef versa_string string; + +namespace std {template <typename T> class vector;} +using std::vector; + +void f(vector<string> v); // expected-note {{candidate function not viable: no known conversion from 'vector<string>' (aka 'std::vector<std::basic_string>') to 'vector<string>' (aka 'std::vector<versa_string>') for 1st argument}} + +namespace std { + class basic_string; + typedef basic_string string; + template <typename T> class vector {}; + void g() { + vector<string> v; + f(v); // expected-error{{no matching function for call to 'f'}} + } +} diff --git a/test/Misc/include-stack-for-note-flag.cpp b/test/Misc/include-stack-for-note-flag.cpp index 328999ded024..cfec5066a05f 100644 --- a/test/Misc/include-stack-for-note-flag.cpp +++ b/test/Misc/include-stack-for-note-flag.cpp @@ -18,11 +18,11 @@ bool macro(int x, int y) { // STACK: note: candidate function not viable // STACK: error: comparison between pointer and integer // STACK: In file included from -// STACK: note: instantiated from: +// STACK: note: expanded from: // STACKLESS: error: no matching function for call to 'foo' // STACKLESS-NOT: In file included from // STACKLESS: note: candidate function not viable // STACKLESS: error: comparison between pointer and integer // STACKLESS-NOT: In file included from -// STACKLESS: note: instantiated from: +// STACKLESS: note: expanded from: diff --git a/test/Misc/macro-backtrace-limit.c b/test/Misc/macro-backtrace-limit.c index 1e512febefa2..ee73c61afc64 100644 --- a/test/Misc/macro-backtrace-limit.c +++ b/test/Misc/macro-backtrace-limit.c @@ -17,16 +17,16 @@ void f(int *ip, float *fp) { // CHECK: macro-backtrace-limit.c:31:7: warning: comparison of distinct pointer types ('int *' and 'float *') // CHECK: if (M12(ip, fp)) { } - // CHECK: macro-backtrace-limit.c:15:19: note: instantiated from: + // CHECK: macro-backtrace-limit.c:15:19: note: expanded from: // CHECK: #define M12(A, B) M11(A, B) - // CHECK: macro-backtrace-limit.c:14:19: note: instantiated from: + // CHECK: macro-backtrace-limit.c:14:19: note: expanded from: // CHECK: #define M11(A, B) M10(A, B) - // CHECK: note: (skipping 7 contexts in backtrace; use -fmacro-backtrace-limit=0 to see all) - // CHECK: macro-backtrace-limit.c:6:18: note: instantiated from: + // CHECK: note: (skipping 7 expansions in backtrace; use -fmacro-backtrace-limit=0 to see all) + // CHECK: macro-backtrace-limit.c:6:18: note: expanded from: // CHECK: #define M3(A, B) M2(A, B) - // CHECK: macro-backtrace-limit.c:5:18: note: instantiated from: + // CHECK: macro-backtrace-limit.c:5:18: note: expanded from: // CHECK: #define M2(A, B) M1(A, B) - // CHECK: macro-backtrace-limit.c:4:23: note: instantiated from: + // CHECK: macro-backtrace-limit.c:4:23: note: expanded from: // CHECK: #define M1(A, B) ((A) < (B)) if (M12(ip, fp)) { } } diff --git a/test/PCH/Inputs/arc.h b/test/PCH/Inputs/arc.h new file mode 100644 index 000000000000..793fc643f23c --- /dev/null +++ b/test/PCH/Inputs/arc.h @@ -0,0 +1,20 @@ +// Header for Objective-C ARC-related PCH tests + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +@interface NSString +@end + +id CreateSomething(); +NSString *CreateNSString(); + +typedef int array0[sizeof((__bridge id)CFCreateSomething())]; +typedef int array1[sizeof((__bridge CFTypeRef)CreateSomething())]; + + diff --git a/test/PCH/Inputs/typo.hpp b/test/PCH/Inputs/typo.hpp new file mode 100644 index 000000000000..c81159500787 --- /dev/null +++ b/test/PCH/Inputs/typo.hpp @@ -0,0 +1,8 @@ +namespace boost { + template<typename F> class function {}; + + namespace graph { + template<typename V, typename E> class adjacency_list { }; + }; +} + diff --git a/test/PCH/arc.m b/test/PCH/arc.m new file mode 100644 index 000000000000..6f7b8704d9ea --- /dev/null +++ b/test/PCH/arc.m @@ -0,0 +1,9 @@ +// Test this without pch. +// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-arc -include %S/Inputs/arc.h -fsyntax-only -emit-llvm -o - %s + +// Test with pch. +// RUN: %clang_cc1 -emit-pch -fblocks -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-arc -x objective-c-header -o %t %S/Inputs/arc.h +// RUN: %clang_cc1 -fblocks -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-arc -include-pch %t -fsyntax-only -emit-llvm -o - %s + +array0 a0; +array1 a1; diff --git a/test/PCH/typo.cpp b/test/PCH/typo.cpp new file mode 100644 index 000000000000..4c8861454d21 --- /dev/null +++ b/test/PCH/typo.cpp @@ -0,0 +1,17 @@ + +// In header: expected-note{{ 'boost::function' declared here}} + + +// In header: expected-note{{ 'boost::graph::adjacency_list' declared here}} + + + +adjacent_list<int, int> g; // expected-error{{no template named 'adjacent_list'; did you mean 'boost::graph::adjacency_list'?}} +Function<int(int)> f; // expected-error{{no template named 'Function'; did you mean 'boost::function'?}} + +// Without PCH +// RUN: %clang_cc1 -include %S/Inputs/typo.hpp -verify %s + +// With PCH +// RUN: %clang_cc1 -x c++-header -emit-pch -o %t %S/Inputs/typo.hpp +// RUN: %clang_cc1 -include-pch %t -verify %s diff --git a/test/PCH/variables.c b/test/PCH/variables.c index 58fd8ae84661..9f90b37d4162 100644 --- a/test/PCH/variables.c +++ b/test/PCH/variables.c @@ -1,18 +1,40 @@ // Test this without pch. -// RUN: %clang_cc1 -include %S/variables.h -fsyntax-only -verify %s +// RUN: %clang_cc1 -include %s -fsyntax-only -verify %s // Test with pch. -// RUN: %clang_cc1 -emit-pch -o %t %S/variables.h +// RUN: %clang_cc1 -emit-pch -o %t %s // RUN: %clang_cc1 -include-pch %t -fsyntax-only -verify %s +#ifndef HEADER +#define HEADER + +extern float y; +extern int *ip, x; + +float z; // expected-note{{previous}} + +int z2 = 17; // expected-note{{previous}} + +#define MAKE_HAPPY(X) X##Happy +int MAKE_HAPPY(Very); // expected-note{{previous definition is here}} + +#define A_MACRO_IN_THE_PCH 492 +#define FUNCLIKE_MACRO(X, Y) X ## Y + +#define PASTE2(x,y) x##y +#define PASTE1(x,y) PASTE2(x,y) +#define UNIQUE(x) PASTE1(x,__COUNTER__) + +int UNIQUE(a); // a0 +int UNIQUE(a); // a1 + +#else + int *ip2 = &x; float *fp = &ip; // expected-warning{{incompatible pointer types}} -// FIXME:variables.h expected-note{{previous}} double z; // expected-error{{redefinition}} -// FIXME:variables.h expected-note{{previous}} int z2 = 18; // expected-error{{redefinition}} double VeryHappy; // expected-error{{redefinition}} -// FIXME:variables.h expected-note{{previous definition is here}} int Q = A_MACRO_IN_THE_PCH; @@ -21,3 +43,5 @@ int R = FUNCLIKE_MACRO(A_MACRO_, IN_THE_PCH); int UNIQUE(a); // a2 int *Arr[] = { &a0, &a1, &a2 }; + +#endif diff --git a/test/PCH/variables.h b/test/PCH/variables.h index c9374297fc02..23d2cd30cceb 100644 --- a/test/PCH/variables.h +++ b/test/PCH/variables.h @@ -23,4 +23,3 @@ int MAKE_HAPPY(Very); int UNIQUE(a); // a0 int UNIQUE(a); // a1 - diff --git a/test/Parser/MicrosoftExtensions.c b/test/Parser/MicrosoftExtensions.c index 2f5da5255b37..2b8451b26dda 100644 --- a/test/Parser/MicrosoftExtensions.c +++ b/test/Parser/MicrosoftExtensions.c @@ -60,5 +60,5 @@ void ms_intrinsics(int a) { __noop(); __assume(a); - + __debugbreak(); } diff --git a/test/Parser/parenthesis-balance.cpp b/test/Parser/parenthesis-balance.cpp new file mode 100644 index 000000000000..5bfa6396039a --- /dev/null +++ b/test/Parser/parenthesis-balance.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +int f(int x) { + if (int foo = f(bar)) {} // expected-error{{use of undeclared identifier 'bar'}} + while (int foo = f(bar)) {} // expected-error{{use of undeclared identifier 'bar'}} + for (int foo = f(bar);;) {} // expected-error{{use of undeclared identifier 'bar'}} + + int bar; + if (int foo = f(bar)) {} + while (int foo = f(bar)) {} + for (int foo = f(bar);;) {} + + return 0; +} + diff --git a/test/Parser/recovery.c b/test/Parser/recovery.c index 1b33f0225bce..0747aecd6c04 100644 --- a/test/Parser/recovery.c +++ b/test/Parser/recovery.c @@ -74,6 +74,11 @@ void foo() { X = 4 // expected-error{{expected ';' after expression}} } +// rdar://9045701 +void test9045701(int x) { +#define VALUE 0 + x = VALUE // expected-error{{expected ';' after expression}} +} // rdar://7980651 typedef int intptr_t; // expected-note {{'intptr_t' declared here}} @@ -84,3 +89,12 @@ void test1(void) { int y = x; int z = y; } + +void test2(int x) { +#define VALUE2 VALUE+VALUE +#define VALUE3 VALUE+0 +#define VALUE4(x) x+0 + x = VALUE2 // expected-error{{expected ';' after expression}} + x = VALUE3 // expected-error{{expected ';' after expression}} + x = VALUE4(0) // expected-error{{expected ';' after expression}} +} diff --git a/test/Parser/switch-recovery.cpp b/test/Parser/switch-recovery.cpp index 0e4dcfa3c637..a1df4261db45 100644 --- a/test/Parser/switch-recovery.cpp +++ b/test/Parser/switch-recovery.cpp @@ -156,3 +156,17 @@ void test12(int x) { } } } + +void missing_statement_case(int x) { + switch (x) { + case 1: + case 0: // expected-error {{label at end of compound statement: expected statement}} + } +} + +void missing_statement_default(int x) { + switch (x) { + case 0: + default: // expected-error {{label at end of compound statement: expected statement}} + } +} diff --git a/test/Preprocessor/include-directive2.c b/test/Preprocessor/include-directive2.c index 5f1ee3cfbc26..b1a9940b4412 100644 --- a/test/Preprocessor/include-directive2.c +++ b/test/Preprocessor/include-directive2.c @@ -1,5 +1,5 @@ // RUN: %clang_cc1 -ffreestanding -Eonly -verify %s -# define HEADER <float.h> +# define HEADER <stdarg.h> # include HEADER diff --git a/test/Preprocessor/init.c b/test/Preprocessor/init.c index b0515b3bba3c..f0920c9dedbd 100644 --- a/test/Preprocessor/init.c +++ b/test/Preprocessor/init.c @@ -15,7 +15,7 @@ // CXX0X:#define __GXX_EXPERIMENTAL_CXX0X__ 1 // CXX0X:#define __GXX_RTTI 1 // CXX0X:#define __GXX_WEAK__ 1 -// CXX0X:#define __cplusplus 199711L +// CXX0X:#define __cplusplus 201103L // CXX0X:#define __private_extern__ extern // // diff --git a/test/Preprocessor/macro_paste_bad.c b/test/Preprocessor/macro_paste_bad.c index 2af0173040dc..0a028a44686e 100644 --- a/test/Preprocessor/macro_paste_bad.c +++ b/test/Preprocessor/macro_paste_bad.c @@ -32,4 +32,3 @@ XX // expected-error {{attempt to use a poisoned identifier}} #define VA __VA_ ## ARGS__ int VA; // expected-warning {{__VA_ARGS__ can only appear in the expansion of a C99 variadic macro}} - diff --git a/test/Preprocessor/macro_paste_hashhash.c b/test/Preprocessor/macro_paste_hashhash.c index e7993cc0a351..f4b03bef2e16 100644 --- a/test/Preprocessor/macro_paste_hashhash.c +++ b/test/Preprocessor/macro_paste_hashhash.c @@ -1,7 +1,11 @@ -// RUN: %clang_cc1 -E %s | grep '^"x ## y";$' +// RUN: %clang_cc1 -E %s | FileCheck %s #define hash_hash # ## # #define mkstr(a) # a #define in_between(a) mkstr(a) #define join(c, d) in_between(c hash_hash d) +// CHECK: "x ## y"; join(x, y); +#define FOO(x) A x B +// CHECK: A ## B; +FOO(##); diff --git a/test/Preprocessor/macro_paste_simple.c b/test/Preprocessor/macro_paste_simple.c index 563d7f44958d..0e62ba46dc96 100644 --- a/test/Preprocessor/macro_paste_simple.c +++ b/test/Preprocessor/macro_paste_simple.c @@ -1,5 +1,14 @@ -// RUN: %clang_cc1 %s -E | grep "barbaz123" +// RUN: %clang_cc1 %s -E | FileCheck %s #define FOO bar ## baz ## 123 -FOO +// CHECK: A: barbaz123 +A: FOO + +// PR9981 +#define M1(A) A +#define M2(X) X +B: M1(M2(##)) + +// CHECK: B: ## + diff --git a/test/Preprocessor/pragma_diagnostic_output.c b/test/Preprocessor/pragma_diagnostic_output.c new file mode 100644 index 000000000000..e847107f8631 --- /dev/null +++ b/test/Preprocessor/pragma_diagnostic_output.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -E %s | FileCheck %s +// CHECK: #pragma GCC diagnostic warning "-Wall" +#pragma GCC diagnostic warning "-Wall" +// CHECK: #pragma GCC diagnostic ignored "-Wall" +#pragma GCC diagnostic ignored "-Wall" +// CHECK: #pragma GCC diagnostic error "-Wall" +#pragma GCC diagnostic error "-Wall" +// CHECK: #pragma GCC diagnostic fatal "-Wall" +#pragma GCC diagnostic fatal "-Wall" +// CHECK: #pragma GCC diagnostic push +#pragma GCC diagnostic push +// CHECK: #pragma GCC diagnostic pop +#pragma GCC diagnostic pop + +// CHECK: #pragma clang diagnostic warning "-Wall" +#pragma clang diagnostic warning "-Wall" +// CHECK: #pragma clang diagnostic ignored "-Wall" +#pragma clang diagnostic ignored "-Wall" +// CHECK: #pragma clang diagnostic error "-Wall" +#pragma clang diagnostic error "-Wall" +// CHECK: #pragma clang diagnostic fatal "-Wall" +#pragma clang diagnostic fatal "-Wall" +// CHECK: #pragma clang diagnostic push +#pragma clang diagnostic push +// CHECK: #pragma clang diagnostic pop +#pragma clang diagnostic pop diff --git a/test/Preprocessor/warn-macro-unused.c b/test/Preprocessor/warn-macro-unused.c index 8a6d7c25ca5f..c33aeb5df9d9 100644 --- a/test/Preprocessor/warn-macro-unused.c +++ b/test/Preprocessor/warn-macro-unused.c @@ -1,5 +1,10 @@ // RUN: %clang_cc1 %s -Wunused-macros -Dfoo -Dfoo -verify +#include "warn-macro-unused.h" + #define unused // expected-warning {{macro is not used}} #define unused unused + +// rdar://9745065 +#undef unused_from_header // no warning diff --git a/test/Preprocessor/warn-macro-unused.h b/test/Preprocessor/warn-macro-unused.h new file mode 100644 index 000000000000..0c2c267fcfd6 --- /dev/null +++ b/test/Preprocessor/warn-macro-unused.h @@ -0,0 +1 @@ +#define unused_from_header diff --git a/test/Sema/arm-neon-types.c b/test/Sema/arm-neon-types.c index 1e8c9bf051e7..4be83da970bd 100644 --- a/test/Sema/arm-neon-types.c +++ b/test/Sema/arm-neon-types.c @@ -18,3 +18,10 @@ float32x2_t test3(uint32x2_t x) { // removed when that is fixed. return vcvt_n_f32_u32(x, 0); // expected-error {{argument should be a value from 1 to 32}} expected-error {{incompatible result type}} } + +typedef signed int vSInt32 __attribute__((__vector_size__(16))); +int32x4_t test4(int32x4_t a, vSInt32 b) { + a += b; + b += a; + return b += a; +} diff --git a/test/Sema/asm.c b/test/Sema/asm.c index d8161c819b99..359431c4f93a 100644 --- a/test/Sema/asm.c +++ b/test/Sema/asm.c @@ -5,7 +5,7 @@ void f() { asm ("foo\n" : : "a" (i + 2)); asm ("foo\n" : : "a" (f())); // expected-error {{invalid type 'void' in asm input}} - + asm ("foo\n" : "=a" (f())); // expected-error {{invalid lvalue in asm output}} asm ("foo\n" : "=a" (i + 2)); // expected-error {{invalid lvalue in asm output}} @@ -21,7 +21,7 @@ void clobbers() { asm ("nop" : : : "0", "%0", "#0"); asm ("nop" : : : "foo"); // expected-error {{unknown register name 'foo' in asm}} asm ("nop" : : : "52"); - asm ("nop" : : : "53"); // expected-error {{unknown register name '53' in asm}} + asm ("nop" : : : "54"); // expected-error {{unknown register name '54' in asm}} asm ("nop" : : : "-1"); // expected-error {{unknown register name '-1' in asm}} asm ("nop" : : : "+1"); // expected-error {{unknown register name '+1' in asm}} } @@ -47,7 +47,7 @@ void test4(const volatile void *addr) // <rdar://problem/6512595> void test5() { - asm("nop" : : "X" (8)); + asm("nop" : : "X" (8)); } // PR3385 @@ -56,7 +56,7 @@ void test6(long i) { } void asm_string_tests(int i) { - asm("%!"); // simple asm string, %! is not an error. + asm("%!"); // simple asm string, %! is not an error. asm("%!" : ); // expected-error {{invalid % escape in inline assembly string}} asm("xyz %" : ); // expected-error {{invalid % escape in inline assembly string}} @@ -64,7 +64,7 @@ void asm_string_tests(int i) { asm ("%[somename]" :: "i"(4)); // expected-error {{unknown symbolic operand name in inline assembly string}} asm ("%[somename" :: "i"(4)); // expected-error {{unterminated symbolic operand name in inline assembly string}} asm ("%[]" :: "i"(4)); // expected-error {{empty symbolic operand name in inline assembly string}} - + // PR3258 asm("%9" :: "i"(4)); // expected-error {{invalid operand number in inline asm string}} asm("%1" : "+r"(i)); // ok, referring to input. @@ -113,3 +113,13 @@ void test11(void) { _Bool b; asm volatile ("movb %%gs:%P2,%b0" : "=q"(b) : "0"(0), "i"(5L)); } + +void test12(void) { + register int cc __asm ("cc"); // expected-error{{unknown register name 'cc' in asm}} +} + +// PR10223 +void test13(void) { + void *esp; + __asm__ volatile ("mov %%esp, %o" : "=r"(esp) : : ); // expected-error {{invalid % escape in inline assembly string}} +} diff --git a/test/Sema/attr-deprecated.c b/test/Sema/attr-deprecated.c index b26171b86b0a..eeef0d757a70 100644 --- a/test/Sema/attr-deprecated.c +++ b/test/Sema/attr-deprecated.c @@ -37,6 +37,8 @@ struct foo { void test1(struct foo *F) { ++F->x; // expected-warning {{'x' is deprecated}} + struct foo f1 = { .x = 17 }; // expected-warning {{'x' is deprecated}} + struct foo f2 = { 17 }; // expected-warning {{'x' is deprecated}} } typedef struct foo foo_dep __attribute__((deprecated)); diff --git a/test/Sema/attr-naked.c b/test/Sema/attr-naked.c index 1ebd78438e2f..d9fa5423d9ef 100644 --- a/test/Sema/attr-naked.c +++ b/test/Sema/attr-naked.c @@ -2,6 +2,10 @@ int a __attribute__((naked)); // expected-warning {{'naked' attribute only applies to functions}} +__attribute__((naked)) int t0(void) { + __asm__ volatile("mov r0, #0"); +} + void t1() __attribute__((naked)); void t2() __attribute__((naked(2))); // expected-error {{attribute takes no arguments}} diff --git a/test/Sema/attr-unavailable-message.c b/test/Sema/attr-unavailable-message.c index a1e047c8176b..9f663fc4efd4 100644 --- a/test/Sema/attr-unavailable-message.c +++ b/test/Sema/attr-unavailable-message.c @@ -16,3 +16,13 @@ void test_foo() { } char test2[__has_feature(attribute_unavailable_with_message) ? 1 : -1]; + +// rdar://9623855 +void unavail(void) __attribute__((__unavailable__)); +void unavail(void) { + // No complains inside an unavailable function. + int ir = foo(1); + double dr = dfoo(1.0); + void (*fp)() = &bar; + double (*fp4)(double) = dfoo; +} diff --git a/test/Sema/attr-weak.c b/test/Sema/attr-weak.c index 41c9fd7165ab..adedf1231f9e 100644 --- a/test/Sema/attr-weak.c +++ b/test/Sema/attr-weak.c @@ -12,3 +12,7 @@ struct __attribute__((weak)) s0 {}; // expected-warning {{'weak' attribute only struct __attribute__((weak_import)) s1 {}; // expected-warning {{'weak_import' attribute only applies to variables and functions}} static int x __attribute__((weak)); // expected-error {{weak declaration cannot have internal linkage}} + +// rdar://9538608 +int C; // expected-note {{previous definition is here}} +extern int C __attribute__((weak_import)); // expected-warning {{an already-declared variable is made a weak_import declaration}} diff --git a/test/Sema/bitfield.c b/test/Sema/bitfield.c index 49c1c7d44361..a1ce894037f7 100644 --- a/test/Sema/bitfield.c +++ b/test/Sema/bitfield.c @@ -32,6 +32,7 @@ int y; struct {unsigned x : 2;} x2; __typeof__((x.x+=1)+1) y; +__typeof__((0,x.x)+1) y; __typeof__(x.x<<1) y; int y; diff --git a/test/Sema/compare.c b/test/Sema/compare.c index 5221b172a602..cd973d4885c7 100644 --- a/test/Sema/compare.c +++ b/test/Sema/compare.c @@ -312,3 +312,18 @@ int rdar8511238() { return 0; return 20; } + +// PR10336 +int test9(int sv, unsigned uv, long slv) { + return sv == (uv ^= slv); // expected-warning {{comparison of integers of different signs: 'int' and 'unsigned int'}} +} + +void test10(void) { + int si; + unsigned int ui; + long sl; + + _Bool b; + b = (si == (ui = sl)); // expected-warning {{comparison of integers of different signs: 'int' and 'unsigned int'}} + b = (si == (ui = sl&15)); +} diff --git a/test/Sema/const-eval.c b/test/Sema/const-eval.c index 56c429c58c2a..bdb40ae54be3 100644 --- a/test/Sema/const-eval.c +++ b/test/Sema/const-eval.c @@ -36,7 +36,7 @@ int g17[(3?:1) - 2]; EVAL_EXPR(18, ((int)((void*)10 + 10)) == 20 ? 1 : -1); struct s { - int a[(int)-1.0f]; // expected-error {{array size is negative}} + int a[(int)-1.0f]; // expected-error {{'a' declared as an array with a negative size}} }; EVAL_EXPR(19, ((int)&*(char*)10 == 10 ? 1 : -1)); diff --git a/test/Sema/conversion-64-32.c b/test/Sema/conversion-64-32.c index aa7282962f8e..112e995102c5 100644 --- a/test/Sema/conversion-64-32.c +++ b/test/Sema/conversion-64-32.c @@ -3,3 +3,13 @@ int test0(long v) { return v; // expected-warning {{implicit conversion loses integer precision}} } + + +// rdar://9546171 +typedef int int4 __attribute__ ((vector_size(16))); +typedef long long long2 __attribute__((__vector_size__(16))); + +int4 test1(long2 a) { + int4 v127 = a; // no warning. + return v127; +} diff --git a/test/Sema/conversion.c b/test/Sema/conversion.c index 1eaf7082d67e..03204c63e965 100644 --- a/test/Sema/conversion.c +++ b/test/Sema/conversion.c @@ -335,3 +335,11 @@ void test_8559831(enum E8559831b value_a, E8559831c value_c) { enum E8559831a a3 = value_d; a3 = value_d; } + +void test26(int si, long sl) { + si = sl % sl; // expected-warning {{implicit conversion loses integer precision: 'long' to 'int'}} + si = sl % si; + si = si % sl; + si = si / sl; + si = sl / si; // expected-warning {{implicit conversion loses integer precision: 'long' to 'int'}} +} diff --git a/test/Sema/ext_vector_casts.c b/test/Sema/ext_vector_casts.c index 75d41ca077ef..848ec1f106f6 100644 --- a/test/Sema/ext_vector_casts.c +++ b/test/Sema/ext_vector_casts.c @@ -37,7 +37,7 @@ static void test() { vec4 /= 5.2f; vec4 %= 4; // expected-error {{invalid operands to binary expression ('float4' and 'int')}} ivec4 %= 4; - ivec4 += vec4; // expected-error {{can't convert between vector values of different size ('float4' and 'int4')}} + ivec4 += vec4; // expected-error {{can't convert between vector values of different size ('int4' and 'float4')}} ivec4 += (int4)vec4; ivec4 -= ivec4; ivec4 |= ivec4; diff --git a/test/Sema/extern-redecl.c b/test/Sema/extern-redecl.c index 067e3c21e4c4..c176725df66b 100644 --- a/test/Sema/extern-redecl.c +++ b/test/Sema/extern-redecl.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only %s +// RUN: %clang_cc1 -fsyntax-only -verify %s // rdar: // 8125274 static int a16[]; // expected-warning {{tentative array definition assumed to have one element}} @@ -7,3 +7,16 @@ void f16(void) { extern int a16[]; } + +// PR10013: Scope of extern declarations extend past enclosing block +extern int PR10013_x; +int PR10013(void) { + int *PR10013_x = 0; + { + extern int PR10013_x; + extern int PR10013_x; + } + + return PR10013_x; // expected-warning{{incompatible pointer to integer conversion}} +} + diff --git a/test/Sema/format-strings.c b/test/Sema/format-strings.c index c78095a04d7b..b47d3ca2616c 100644 --- a/test/Sema/format-strings.c +++ b/test/Sema/format-strings.c @@ -358,3 +358,17 @@ void pr9314() { printf(__func__); // no-warning } +int printf(const char * restrict, ...) __attribute__((__format__ (__printf__, 1, 2))); + +void rdar9612060(void) { + printf("%s", 2); // expected-warning{{conversion specifies type 'char *' but the argument has type 'int'}} +} + +void check_char(unsigned char x, signed char y) { + printf("%c", y); // no-warning + printf("%hhu", x); // no-warning + printf("%hhi", y); // no-warning + printf("%hhi", x); // no-warning + printf("%c", x); // no-warning + printf("%hhu", y); // no-warning +} diff --git a/test/Sema/i-c-e.c b/test/Sema/i-c-e.c index 4d7007cd077c..1aac51e204f1 100644 --- a/test/Sema/i-c-e.c +++ b/test/Sema/i-c-e.c @@ -1,4 +1,4 @@ -// RUN: %clang %s -ffreestanding -fsyntax-only -Xclang -verify -pedantic -fpascal-strings +// RUN: %clang %s -ffreestanding -fsyntax-only -Xclang -verify -pedantic -fpascal-strings -std=c99 #include <stdint.h> #include <limits.h> diff --git a/test/Sema/incomplete-decl.c b/test/Sema/incomplete-decl.c index e5b6d5f0da6d..0214a0c6ce4c 100644 --- a/test/Sema/incomplete-decl.c +++ b/test/Sema/incomplete-decl.c @@ -22,7 +22,7 @@ void func() { } int h[]; // expected-warning {{tentative array definition assumed to have one element}} -int (*i)[] = &h+1; // expected-error {{arithmetic on pointer to incomplete type 'int (*)[]'}} +int (*i)[] = &h+1; // expected-error {{arithmetic on a pointer to an incomplete type 'int []'}} struct bar j = {1}; // expected-error {{variable has incomplete type 'struct bar'}} \ expected-note {{forward declaration of 'struct bar'}} diff --git a/test/Sema/inline-redef.c b/test/Sema/inline-redef.c new file mode 100644 index 000000000000..4a46e1193e1f --- /dev/null +++ b/test/Sema/inline-redef.c @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -std=gnu89 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s + +#if __STDC_VERSION__ >= 199901 +#define GNU_INLINE __attribute((__gnu_inline__)) +#else +#define GNU_INLINE +#endif + +// PR5253 +// rdar://9559708 (same extension in C99 mode) +// GNU Extension: check that we can redefine an extern inline function +GNU_INLINE extern inline int f(int a) {return a;} +int f(int b) {return b;} // expected-note{{previous definition is here}} +// And now check that we can't redefine a normal function +int f(int c) {return c;} // expected-error{{redefinition of 'f'}} + +// Check that we can redefine an extern inline function as a static function +GNU_INLINE extern inline int g(int a) {return a;} +static int g(int b) {return b;} + +// Check that we ensure the types of the two definitions are the same +GNU_INLINE extern inline int h(int a) {return a;} // expected-note{{previous definition is here}} +int h(short b) {return b;} // expected-error{{conflicting types for 'h'}} diff --git a/test/Sema/inline.c b/test/Sema/inline.c index b4795d3d936f..3c99f2433783 100644 --- a/test/Sema/inline.c +++ b/test/Sema/inline.c @@ -1,22 +1,6 @@ -// RUN: %clang_cc1 -std=gnu89 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -verify %s // Check that we don't allow illegal uses of inline inline int a; // expected-error{{'inline' can only appear on functions}} typedef inline int b; // expected-error{{'inline' can only appear on functions}} int d(inline int a); // expected-error{{'inline' can only appear on functions}} - -// PR5253 -// GNU Extension: check that we can redefine an extern inline function -extern inline int f(int a) {return a;} -int f(int b) {return b;} // expected-note{{previous definition is here}} -// And now check that we can't redefine a normal function -int f(int c) {return c;} // expected-error{{redefinition of 'f'}} - -// Check that we can redefine an extern inline function as a static function -extern inline int g(int a) {return a;} -static int g(int b) {return b;} - -// Check that we ensure the types of the two definitions are the same -extern inline int h(int a) {return a;} // expected-note{{previous definition is here}} -int h(short b) {return b;} // expected-error{{conflicting types for 'h'}} - diff --git a/test/Sema/no-format-y2k-turnsoff-format.c b/test/Sema/no-format-y2k-turnsoff-format.c new file mode 100644 index 000000000000..bd06e506ce09 --- /dev/null +++ b/test/Sema/no-format-y2k-turnsoff-format.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -verify -fsyntax-only -Wformat -Wno-format-y2k +// rdar://9504680 + +void foo(const char *, ...) __attribute__((__format__ (__printf__, 1, 2))); + +void bar(unsigned int a) { + foo("%s", a); // expected-warning {{conversion specifies type 'char *' but the argument has type 'unsigned int'}} +} + diff --git a/test/Sema/nonnull.c b/test/Sema/nonnull.c new file mode 100644 index 000000000000..cea8e3d0ddc4 --- /dev/null +++ b/test/Sema/nonnull.c @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only %s +// rdar://9584012 + +typedef struct { + char *str; +} Class; + +typedef union { + Class *object; +} Instance __attribute__((transparent_union)); + +__attribute__((nonnull(1))) void Class_init(Instance this, char *str) { + this.object->str = str; +} + +int main(void) { + Class *obj; + Class_init(0, "Hello World"); // expected-warning {{null passed to a callee which requires a non-null argument}} + Class_init(obj, "Hello World"); +} + diff --git a/test/Sema/paren-list-expr-type.cpp b/test/Sema/paren-list-expr-type.cpp new file mode 100644 index 000000000000..ad5b7fbf918e --- /dev/null +++ b/test/Sema/paren-list-expr-type.cpp @@ -0,0 +1,17 @@ +// RUN: %clang -cc1 -ast-dump %s | not grep NULL +// Makes sure that we don't introduce null types when handling +// ParenListExpr. + +template<typename T> class X { void f() { X x(*this); } }; + +template<typename T> class Y { Y() : t(1) {} T t; }; + +template<typename T> class Z { Z() : b(true) {} const bool b; }; + +template<typename T> class A : public Z<T> { A() : Z<T>() {} }; + +class C {}; +template<typename T> class D : public C { D(): C() {} }; + +void f() { (int)(1, 2); } + diff --git a/test/Sema/parentheses.c b/test/Sema/parentheses.c index fa3c3c26b042..13ea3ecbe16a 100644 --- a/test/Sema/parentheses.c +++ b/test/Sema/parentheses.c @@ -26,6 +26,12 @@ void bitwise_rel(unsigned i) { (void)(i == 1 | i == 2 | i == 3); (void)(i != 1 & i != 2 & i != 3); + (void)(i & i | i); // expected-warning {{'&' within '|'}} \ + // expected-note {{place parentheses around the '&' expression to silence this warning}} + + (void)(i | i & i); // expected-warning {{'&' within '|'}} \ + // expected-note {{place parentheses around the '&' expression to silence this warning}} + (void)(i || i && i); // expected-warning {{'&&' within '||'}} \ // expected-note {{place parentheses around the '&&' expression to silence this warning}} @@ -42,21 +48,21 @@ void bitwise_rel(unsigned i) { _Bool someConditionFunc(); void conditional_op(int x, int y, _Bool b) { - (void)(x + someConditionFunc() ? 1 : 2); // expected-warning {{?: has lower precedence than +}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the + expression to silence this warning}} + (void)(x + someConditionFunc() ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '+'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '+' expression to silence this warning}} - (void)(x - b ? 1 : 2); // expected-warning {{?: has lower precedence than -}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the - expression to silence this warning}} + (void)(x - b ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '-'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '-' expression to silence this warning}} - (void)(x * (x == y) ? 1 : 2); // expected-warning {{?: has lower precedence than *}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the * expression to silence this warning}} + (void)(x * (x == y) ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '*'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '*' expression to silence this warning}} - (void)(x / !x ? 1 : 2); // expected-warning {{?: has lower precedence than /}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the / expression to silence this warning}} + (void)(x / !x ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '/'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '/' expression to silence this warning}} (void)(x % 2 ? 1 : 2); // no warning diff --git a/test/Sema/parentheses.cpp b/test/Sema/parentheses.cpp index a25f2a0ce7b8..252455dcad49 100644 --- a/test/Sema/parentheses.cpp +++ b/test/Sema/parentheses.cpp @@ -4,17 +4,17 @@ bool someConditionFunc(); void conditional_op(int x, int y, bool b) { - (void)(x + someConditionFunc() ? 1 : 2); // expected-warning {{?: has lower precedence than +}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the + expression to silence this warning}} + (void)(x + someConditionFunc() ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '+'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '+' expression to silence this warning}} - (void)(x - b ? 1 : 2); // expected-warning {{?: has lower precedence than -}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the - expression to silence this warning}} + (void)(x - b ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '-'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '-' expression to silence this warning}} - (void)(x * (x == y) ? 1 : 2); // expected-warning {{?: has lower precedence than *}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the * expression to silence this warning}} + (void)(x * (x == y) ? 1 : 2); // expected-warning {{operator '?:' has lower precedence than '*'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '*' expression to silence this warning}} } class Stream { @@ -25,7 +25,21 @@ public: }; void f(Stream& s, bool b) { - (void)(s << b ? "foo" : "bar"); // expected-warning {{?: has lower precedence than <<}} \ - // expected-note {{place parentheses around the ?: expression to evaluate it first}} \ - // expected-note {{place parentheses around the << expression to silence this warning}} + (void)(s << b ? "foo" : "bar"); // expected-warning {{operator '?:' has lower precedence than '<<'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '<<' expression to silence this warning}} +} + +struct S { + operator int() { return 42; } + friend S operator+(const S &lhs, bool) { return S(); } +}; + +void test(S *s, bool (S::*m_ptr)()) { + (void)(*s + true ? "foo" : "bar"); // expected-warning {{operator '?:' has lower precedence than '+'}} \ + // expected-note {{place parentheses around the '?:' expression to evaluate it first}} \ + // expected-note {{place parentheses around the '+' expression to silence this warning}} + + // Don't crash on unusual member call expressions. + (void)((s->*m_ptr)() ? "foo" : "bar"); } diff --git a/test/Sema/pointer-addition.c b/test/Sema/pointer-addition.c index aa425a7fd9d9..21ce63b4381f 100644 --- a/test/Sema/pointer-addition.c +++ b/test/Sema/pointer-addition.c @@ -3,18 +3,19 @@ typedef struct S S; // expected-note 3 {{forward declaration of 'struct S'}} void a(S* b, void* c) { void (*fp)(int) = 0; - b++; // expected-error {{arithmetic on pointer to incomplete type}} - b += 1; // expected-error {{arithmetic on pointer to incomplete type}} - c++; // expected-warning {{use of GNU void* extension}} - c += 1; // expected-warning {{use of GNU void* extension}} - c--; // expected-warning {{use of GNU void* extension}} - c -= 1; // expected-warning {{use of GNU void* extension}} - (void) c[1]; // expected-warning {{use of GNU void* extension}} - b = 1+b; // expected-error {{arithmetic on pointer to incomplete type}} + b++; // expected-error {{arithmetic on a pointer to an incomplete type}} + b += 1; // expected-error {{arithmetic on a pointer to an incomplete type}} + c++; // expected-warning {{arithmetic on a pointer to void is a GNU extension}} + c += 1; // expected-warning {{arithmetic on a pointer to void is a GNU extension}} + c--; // expected-warning {{arithmetic on a pointer to void is a GNU extension}} + c -= 1; // expected-warning {{arithmetic on a pointer to void is a GNU extension}} + (void) c[1]; // expected-warning {{subscript of a pointer to void is a GNU extension}} + b = 1+b; // expected-error {{arithmetic on a pointer to an incomplete type}} /* The next couple tests are only pedantic warnings in gcc */ void (*d)(S*,void*) = a; - d += 1; // expected-warning {{arithmetic on pointer to function type 'void (*)(S *, void *)' is a GNU extension}} - d++; // expected-warning {{arithmetic on pointer to function type 'void (*)(S *, void *)' is a GNU extension}}} - d--; // expected-warning {{arithmetic on pointer to function type 'void (*)(S *, void *)' is a GNU extension}} - d -= 1; // expected-warning {{arithmetic on pointer to function type 'void (*)(S *, void *)' is a GNU extension}} + d += 1; // expected-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' is a GNU extension}} + d++; // expected-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' is a GNU extension}} + d--; // expected-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' is a GNU extension}} + d -= 1; // expected-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' is a GNU extension}} + (void)(1 + d); // expected-warning {{arithmetic on a pointer to the function type 'void (S *, void *)' is a GNU extension}} } diff --git a/test/Sema/pointer-subtract-compat.c b/test/Sema/pointer-subtract-compat.c index 70340c6a4cd3..b801f81473b3 100644 --- a/test/Sema/pointer-subtract-compat.c +++ b/test/Sema/pointer-subtract-compat.c @@ -7,5 +7,5 @@ int a(char* a, rchar* b) { // <rdar://problem/6520707> void f0(void (*fp)(void)) { - int x = fp - fp; // expected-warning{{arithmetic on pointer to function type 'void (*)(void)' is a GNU extension}} + int x = fp - fp; // expected-warning{{arithmetic on pointers to the function type 'void (void)' is a GNU extension}} } diff --git a/test/Sema/shift.c b/test/Sema/shift.c index 28407be079f4..142d83c43d92 100644 --- a/test/Sema/shift.c +++ b/test/Sema/shift.c @@ -38,7 +38,7 @@ void test() { int i; i = 1 << (WORD_BIT - 2); i = 2 << (WORD_BIT - 1); // expected-warning {{bits to represent, but 'int' only has}} - i = 1 << (WORD_BIT - 1); // expected-warning {{overrides the sign bit of the shift expression}} + i = 1 << (WORD_BIT - 1); // expected-warning {{sets the sign bit of the shift expression}} i = -1 << (WORD_BIT - 1); i = 0 << (WORD_BIT - 1); i = (char)1 << (WORD_BIT - 2); diff --git a/test/Sema/sign-conversion.c b/test/Sema/sign-conversion.c new file mode 100644 index 000000000000..4b1ee7542158 --- /dev/null +++ b/test/Sema/sign-conversion.c @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wsign-conversion %s + +// PR9345: make a subgroup of -Wconversion for signedness changes + +void test(int x) { + unsigned t0 = x; // expected-warning {{implicit conversion changes signedness}} + unsigned t1 = (t0 == 5 ? x : 0); // expected-warning {{operand of ? changes signedness}} +} diff --git a/test/Sema/struct-decl.c b/test/Sema/struct-decl.c index e1c7073e01b2..6070e875f5a4 100644 --- a/test/Sema/struct-decl.c +++ b/test/Sema/struct-decl.c @@ -6,7 +6,7 @@ struct bar { struct foo { char name[(int)&((struct bar *)0)->n]; - char name2[(int)&((struct bar *)0)->n - 1]; //expected-error{{array size is negative}} + char name2[(int)&((struct bar *)0)->n - 1]; //expected-error{{'name2' declared as an array with a negative size}} }; // PR3430 diff --git a/test/Sema/typecheck-binop.c b/test/Sema/typecheck-binop.c index 712dad279db5..be52d0bd8444 100644 --- a/test/Sema/typecheck-binop.c +++ b/test/Sema/typecheck-binop.c @@ -7,15 +7,15 @@ int sub1(int *a, double *b) { } void *sub2(struct incomplete *P) { - return P-4; /* expected-error{{subtraction of pointer 'struct incomplete *' requires pointee to be a complete object type}} */ + return P-4; /* expected-error{{arithmetic on a pointer to an incomplete type 'struct incomplete'}} */ } void *sub3(void *P) { - return P-4; /* expected-warning{{GNU void* extension}} */ + return P-4; /* expected-warning{{arithmetic on a pointer to void is a GNU extension}} */ } int sub4(void *P, void *Q) { - return P-Q; /* expected-warning{{GNU void* extension}} */ + return P-Q; /* expected-warning{{arithmetic on pointers to void is a GNU extension}} */ } int sub5(void *P, int *Q) { diff --git a/test/Sema/typedef-variable-type.c b/test/Sema/typedef-variable-type.c index b805b1e0578f..8a7ee8b911c0 100644 --- a/test/Sema/typedef-variable-type.c +++ b/test/Sema/typedef-variable-type.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 %s -verify -fsyntax-only -pedantic -Wno-typedef-redefinition +// RUN: %clang_cc1 %s -verify -fsyntax-only -pedantic -Wno-typedef-redefinition -std=c99 // Make sure we accept a single typedef typedef int (*a)[!.0]; // expected-warning{{size of static array must be an integer constant expression}} diff --git a/test/Sema/uninit-variables.c b/test/Sema/uninit-variables.c index b70a29519c41..0caab3df18c0 100644 --- a/test/Sema/uninit-variables.c +++ b/test/Sema/uninit-variables.c @@ -352,3 +352,18 @@ int test52(int a, int b) { } return x; // expected-warning {{variable 'x' may be uninitialized when used here}} } + +// This CFG caused the uninitialized values warning to inf-loop. +extern int PR10379_g(); +void PR10379_f(int *len) { + int new_len; // expected-note {{variable 'new_len' is declared here}} expected-note{{add initialization to silence this warning}} + for (int i = 0; i < 42 && PR10379_g() == 0; i++) { + if (PR10379_g() == 1) + continue; + if (PR10379_g() == 2) + PR10379_f(&new_len); + else if (PR10379_g() == 3) + PR10379_f(&new_len); + *len += new_len; // expected-warning {{variable 'new_len' may be uninitialized when used here}} + } +} diff --git a/test/Sema/varargs.c b/test/Sema/varargs.c index e399f89ae0aa..07081edd1d95 100644 --- a/test/Sema/varargs.c +++ b/test/Sema/varargs.c @@ -61,3 +61,18 @@ void f7(int a, ...) { __builtin_va_end(ap); } +void f8(int a, ...) { + __builtin_va_list ap; + __builtin_va_start(ap, a); + (void)__builtin_va_arg(ap, void); // expected-error {{second argument to 'va_arg' is of incomplete type 'void'}} + __builtin_va_end(ap); +} + +enum E { x = -1, y = 2, z = 10000 }; +void f9(__builtin_va_list args) +{ + (void)__builtin_va_arg(args, float); // expected-warning {{second argument to 'va_arg' is of promotable type 'float'}} + (void)__builtin_va_arg(args, enum E); // Don't warn here in C + (void)__builtin_va_arg(args, short); // expected-warning {{second argument to 'va_arg' is of promotable type 'short'}} + (void)__builtin_va_arg(args, char); // expected-warning {{second argument to 'va_arg' is of promotable type 'char'}} +} diff --git a/test/Sema/vla.c b/test/Sema/vla.c index ebf9b889ee2a..fd7a6bf3d72f 100644 --- a/test/Sema/vla.c +++ b/test/Sema/vla.c @@ -42,7 +42,7 @@ void f3() } // PR3663 -static const unsigned array[((2 * (int)((((4) / 2) + 1.0/3.0) * (4) - 1e-8)) + 1)]; // expected-warning {{size of static array must be an integer constant expression}} +static const unsigned array[((2 * (int)((((4) / 2) + 1.0/3.0) * (4) - 1e-8)) + 1)]; // expected-warning {{variable length array folded to constant array as an extension}} int a[*]; // expected-error {{star modifier used outside of function prototype}} int f4(int a[*][*]); @@ -53,7 +53,7 @@ int pr2044b; int (*pr2044c(void))[pr2044b]; // expected-error {{variably modified type}} const int f5_ci = 1; -void f5() { char a[][f5_ci] = {""}; } // expected-error {{variable-sized object may not be initialized}} +void f5() { char a[][f5_ci] = {""}; } // expected-warning {{variable length array folded to constant array as an extension}} // PR5185 void pr5185(int a[*]); diff --git a/test/Sema/warn-sizeof-arrayarg.c b/test/Sema/warn-sizeof-arrayarg.c new file mode 100644 index 000000000000..ba8a5fa975a4 --- /dev/null +++ b/test/Sema/warn-sizeof-arrayarg.c @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +typedef int Arr[10]; + +typedef int trungl_int; + +void f(int a[10], Arr arr) { // \ +// expected-note {{declared here}} \ +// expected-note {{declared here}} \ +// expected-note {{declared here}} \ +// expected-note {{declared here}} + + /* Should warn. */ + (void)sizeof(a); // \ + // expected-warning{{sizeof on array function parameter will return size of 'int *' instead of 'int [10]'}} + (void)sizeof((((a)))); // \ + // expected-warning{{sizeof on array function parameter will return size of 'int *' instead of 'int [10]'}} + (void)sizeof a; // \ + // expected-warning{{sizeof on array function parameter will return size of 'int *' instead of 'int [10]'}} + (void)sizeof arr; // \ + // expected-warning{{sizeof on array function parameter will return size of 'int *' instead of 'Arr' (aka 'int [10]')}} + + /* Shouldn't warn. */ + int b[10]; + (void)sizeof b; + Arr brr; + (void)sizeof brr; + (void)sizeof(Arr); + (void)sizeof(int); +} diff --git a/test/Sema/x86-builtin-palignr.c b/test/Sema/x86-builtin-palignr.c index 83719a379fa6..6e1303c8a105 100644 --- a/test/Sema/x86-builtin-palignr.c +++ b/test/Sema/x86-builtin-palignr.c @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -ffreestanding -fsyntax-only -target-feature +ssse3 -verify -triple x86_64-pc-linux-gnu %s -// RUN: %clang_cc1 -ffreestanding -fsyntax-only -target-feature +ssse3 -verify -triple i686-apple-darwin10 %s +// RUN: %clang_cc1 -ffreestanding -fsyntax-only -target-feature +ssse3 -target-feature +mmx -verify -triple x86_64-pc-linux-gnu %s +// RUN: %clang_cc1 -ffreestanding -fsyntax-only -target-feature +ssse3 -target-feature +mmx -verify -triple i686-apple-darwin10 %s #include <tmmintrin.h> diff --git a/test/SemaCXX/PR10243.cpp b/test/SemaCXX/PR10243.cpp new file mode 100644 index 000000000000..9a5851049803 --- /dev/null +++ b/test/SemaCXX/PR10243.cpp @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++0x %s + +struct S; // expected-note 4{{forward declaration of 'S'}} + +struct T0 { + S s; // expected-error{{field has incomplete type 'S'}} + T0() = default; +}; + +struct T1 { + S s; // expected-error{{field has incomplete type 'S'}} + T1(T1&) = default; +}; + +struct T2 { + S s; // expected-error{{field has incomplete type 'S'}} + T2& operator=(T2&) = default; +}; + +struct T3 { + S s; // expected-error{{field has incomplete type 'S'}} + ~T3() = default; +}; diff --git a/test/SemaCXX/PR7410.cpp b/test/SemaCXX/PR7410.cpp new file mode 100644 index 000000000000..6d2cda90b1d3 --- /dev/null +++ b/test/SemaCXX/PR7410.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +struct BaseReturn {}; + +struct Base { + virtual BaseReturn Foo() = 0; // expected-note{{overridden virtual function is here}} +}; +struct X {}; +struct Derived : Base { + X Foo(); // expected-error{{virtual function 'Foo' has a different return type ('X') than the function it overrides (which has return type 'BaseReturn')}} +}; + +Derived d; diff --git a/test/SemaCXX/PR9459.cpp b/test/SemaCXX/PR9459.cpp index dfb242dc5952..2b96f34e8a72 100644 --- a/test/SemaCXX/PR9459.cpp +++ b/test/SemaCXX/PR9459.cpp @@ -3,5 +3,5 @@ // Don't crash. template<typename>struct ae_same; -template<typename>struct ts{}ap() +template<typename>struct ts{}ap() // expected-error {{expected ';' after struct}} expected-error {{requires a type specifier}} {ts<a>::ap<ae_same<int>::&ae_same<>>::p(a); }; // expected-error {{use of undeclared identifier 'a'}} diff --git a/test/SemaCXX/alignof-sizeof-reference.cpp b/test/SemaCXX/alignof-sizeof-reference.cpp index b517e43cf1ff..93ba203ae112 100644 --- a/test/SemaCXX/alignof-sizeof-reference.cpp +++ b/test/SemaCXX/alignof-sizeof-reference.cpp @@ -13,3 +13,9 @@ void f(int); // expected-note{{candidate function}} void g() { sizeof(&f); // expected-error{{cannot resolve overloaded function 'f' from context}} } + +template<typename T> void f_template(); // expected-note{{candidate function}} +template<typename T> void f_template(T*); // expected-note{{candidate function}} +void rdar9659191() { + (void)alignof(f_template<int>); // expected-error{{cannot resolve overloaded function 'f_template' from context}} +} diff --git a/test/SemaCXX/attr-unavailable.cpp b/test/SemaCXX/attr-unavailable.cpp index 5f34ed990413..2d82668a205a 100644 --- a/test/SemaCXX/attr-unavailable.cpp +++ b/test/SemaCXX/attr-unavailable.cpp @@ -28,3 +28,12 @@ void bar() { foo(); // expected-error {{call to unavailable function 'foo': not available - replaced}} } } + +void unavail(short* sp) __attribute__((__unavailable__)); +void unavail(short* sp) { + // No complains inside an unavailable function. + int &ir = foo(1); + double &dr = foo(1.0); + foo(sp); + foo(); +} diff --git a/test/SemaCXX/c99-variable-length-array.cpp b/test/SemaCXX/c99-variable-length-array.cpp index 98df1dbfe81e..3f1d6a8a55c0 100644 --- a/test/SemaCXX/c99-variable-length-array.cpp +++ b/test/SemaCXX/c99-variable-length-array.cpp @@ -121,3 +121,12 @@ namespace PR8209 { (void)new vla_type; // expected-error{{variably}} } } + +namespace rdar8733881 { // rdar://8733881 + +static const int k_cVal3 = (int)(1000*0.2f); + int f() { + // Ok, fold to a constant size array as an extension. + char rgch[k_cVal3] = {0}; + } +} diff --git a/test/SemaCXX/condition.cpp b/test/SemaCXX/condition.cpp index 61d1762be85f..3441bae67076 100644 --- a/test/SemaCXX/condition.cpp +++ b/test/SemaCXX/condition.cpp @@ -16,8 +16,9 @@ void test() { for (;s;) ; // expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} switch (s) {} // expected-error {{statement requires expression of integer type ('struct S' invalid)}} - while (struct S {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{no viable conversion}} expected-error {{value of type 'struct S' is not contextually convertible to 'bool'}} expected-note{{candidate constructor (the implicit copy constructor)}} - while (struct {} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{no viable conversion}} expected-error {{not contextually convertible to 'bool'}} expected-note{{candidate constructor (the implicit copy constructor)}} + while (struct NewS *x=0) ; + while (struct S {} *x=0) ; // expected-error {{types may not be defined in conditions}} + while (struct {} *x=0) ; // expected-error {{types may not be defined in conditions}} switch (enum {E} x=0) ; // expected-error {{types may not be defined in conditions}} expected-error {{cannot initialize}} \ // expected-warning{{enumeration value 'E' not handled in switch}} diff --git a/test/SemaCXX/conversion-function.cpp b/test/SemaCXX/conversion-function.cpp index aa47ae0f4825..a7a178244c80 100644 --- a/test/SemaCXX/conversion-function.cpp +++ b/test/SemaCXX/conversion-function.cpp @@ -378,3 +378,17 @@ namespace PR9336 { generic_list<generic_list<int> > l; array<array<int> > a = l; } + +namespace PR8800 { + struct A; + struct C { + operator A&(); + }; + void f() { + C c; + A& a1(c); + A& a2 = c; + A& a3 = static_cast<A&>(c); + A& a4 = (A&)c; + } +} diff --git a/test/SemaCXX/copy-assignment.cpp b/test/SemaCXX/copy-assignment.cpp index f3dadc445040..7aca9d6a80ab 100644 --- a/test/SemaCXX/copy-assignment.cpp +++ b/test/SemaCXX/copy-assignment.cpp @@ -99,15 +99,17 @@ void test() { // <rdar://problem/8315440>: Don't crash // FIXME: the recovery here is really bad. -namespace test1 { // expected-note{{to match this '{'}} - template<typename T> class A : public unknown::X { // expected-error {{undeclared identifier 'unknown'}} expected-error {{expected class name}} \ - // expected-note{{template parameter is declared here}} +namespace test1 { + template<typename T> class A : public unknown::X { // expected-error {{undeclared identifier 'unknown'}} expected-error {{expected class name}} A(UndeclaredType n) : X(n) {} // expected-error{{expected ')'}} expected-note{{to match this '('}} \ - // expected-error{{use of undeclared identifier 'n'}} + // expected-error{{use of undeclared identifier 'n'}} \ + // expected-error{{expected ';' at end of declaration list}} \ + // expected-error{{field has incomplete type 'test1::A<char>'}} }; - template<typename T> class B : public A<T> { // expected-error{{declaration of 'T' shadows template parameter}} + template<typename T> class B : public A<T> { virtual void foo() {} }; - extern template class A<char>; // expected-error{{expected member name or ';' after declaration specifiers}} - extern template class B<char>; // expected-error{{expected member name or ';' after declaration specifiers}} -} // expected-error{{expected ';' after class}} // expected-error{{expected '}'}} + extern template class A<char>; // expected-note {{in instantiation of template class 'test1::A<char>' requested here}} \ + // expected-note {{definition of 'test1::A<char>' is not complete until the closing '}'}} + extern template class B<char>; +} diff --git a/test/SemaCXX/crashes.cpp b/test/SemaCXX/crashes.cpp index c75b0401fed6..b77248ef4104 100644 --- a/test/SemaCXX/crashes.cpp +++ b/test/SemaCXX/crashes.cpp @@ -95,3 +95,12 @@ namespace PR9026 { Write(x); } } + +namespace PR10270 { + template<typename T> class C; + template<typename T> void f() { + if (C<T> == 1) // expected-error{{expected unqualified-id}} \ + // expected-error{{invalid '==' at end of declaration}} + return; + } +} diff --git a/test/SemaCXX/decltype.cpp b/test/SemaCXX/decltype.cpp index d4ef7e33a1d1..f61a92b71e1e 100644 --- a/test/SemaCXX/decltype.cpp +++ b/test/SemaCXX/decltype.cpp @@ -16,3 +16,8 @@ void test_f2() { float &fr = f2(AC().a); } +namespace pr10154 { + class A{ + A(decltype(nullptr) param); + }; +}
\ No newline at end of file diff --git a/test/SemaCXX/enum-scoped.cpp b/test/SemaCXX/enum-scoped.cpp index fc871cf37920..73e7578ecb6e 100644 --- a/test/SemaCXX/enum-scoped.cpp +++ b/test/SemaCXX/enum-scoped.cpp @@ -119,3 +119,26 @@ namespace rdar9366066 { x % 8; // expected-error{{invalid operands to binary expression ('rdar9366066::X' and 'int')}} } } + +// Part 1 of PR10264 +namespace test5 { + namespace ns { + typedef unsigned Atype; + enum A : Atype; + } + enum ns::A : ns::Atype { + x, y, z + }; +} + +// Part 2 of PR10264 +namespace test6 { + enum A : unsigned; + struct A::a; // expected-error {{incomplete type 'test6::A' named in nested name specifier}} + enum A::b; // expected-error {{incomplete type 'test6::A' named in nested name specifier}} + int A::c; // expected-error {{incomplete type 'test6::A' named in nested name specifier}} + void A::d(); // expected-error {{incomplete type 'test6::A' named in nested name specifier}} + void test() { + (void) A::e; // expected-error {{incomplete type 'test6::A' named in nested name specifier}} + } +} diff --git a/test/SemaCXX/expressions.cpp b/test/SemaCXX/expressions.cpp index 95ece48e51fe..8a294face380 100644 --- a/test/SemaCXX/expressions.cpp +++ b/test/SemaCXX/expressions.cpp @@ -63,3 +63,25 @@ int test2(int x) { return x && (-1); // expected-warning {{use of logical && with constant operand; switch to bitwise & or remove constant}} return x && (5); // expected-warning {{use of logical && with constant operand; switch to bitwise & or remove constant}} } + +template<unsigned int A, unsigned int B> struct S +{ + enum { + e1 = A && B, + e2 = A && 7 // expected-warning {{use of logical && with constant operand; switch to bitwise & or remove constant}} + }; + + int foo() { + int x = A && B; + int y = B && 3; // expected-warning {{use of logical && with constant operand; switch to bitwise & or remove constant}} + + return x + y; + } +}; + +void test3() { + S<5, 8> s1; + S<2, 7> s2; + (void)s1.foo(); + (void)s2.foo(); +} diff --git a/test/SemaCXX/for-range-unused.cpp b/test/SemaCXX/for-range-unused.cpp new file mode 100644 index 000000000000..7b7d84d3f9c8 --- /dev/null +++ b/test/SemaCXX/for-range-unused.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++0x -Wunused + +// PR9968: We used to warn that __range is unused in a dependent for-range. + +template <typename T> + struct Vector { + void doIt() { + int a; // expected-warning {{unused variable 'a'}} + + for (auto& e : elements) + ; + } + + T elements[10]; + }; + + +int main(int, char**) { + Vector<int> vector; + vector.doIt(); +} diff --git a/test/SemaCXX/function-overload-typo-crash.cpp b/test/SemaCXX/function-overload-typo-crash.cpp new file mode 100644 index 000000000000..0fea312a97f2 --- /dev/null +++ b/test/SemaCXX/function-overload-typo-crash.cpp @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// PR10283 +void min(); +void min(int); + +template <typename T> void max(T); + +void f() { + fin(); //expected-error {{use of undeclared identifier 'fin'; did you mean 'min'}} + fax(0); //expected-error {{use of undeclared identifier 'fax'; did you mean 'max'}} +} diff --git a/test/SemaCXX/generalized-initializers.cpp b/test/SemaCXX/generalized-initializers.cpp index ec37a0c9ac18..6e2bee7e301d 100644 --- a/test/SemaCXX/generalized-initializers.cpp +++ b/test/SemaCXX/generalized-initializers.cpp @@ -85,23 +85,52 @@ namespace integral { namespace objects { + struct X1 { X1(int); }; + struct X2 { explicit X2(int); }; + template <int N> struct A { A() { static_assert(N == 0, ""); } A(int, double) { static_assert(N == 1, ""); } - A(int, int) { static_assert(N == 2, ""); } A(std::initializer_list<int>) { static_assert(N == 3, ""); } }; - void initialization() { + template <int N> + struct D { + D(std::initializer_list<int>) { static_assert(N == 0, ""); } // expected-note 1 {{candidate}} + D(std::initializer_list<double>) { static_assert(N == 1, ""); } // expected-note 1 {{candidate}} + }; + + template <int N> + struct E { + E(int, int) { static_assert(N == 0, ""); } + E(X1, int) { static_assert(N == 1, ""); } + }; + + void overload_resolution() { { A<0> a{}; } { A<0> a = {}; } - { A<1> a{1, 1.0}; } - { A<1> a = {1, 1.0}; } + // Narrowing conversions don't affect viability. The next two choose + // the initializer_list constructor. + { A<3> a{1, 1.0}; } // expected-error {{narrowing conversion}} + { A<3> a = {1, 1.0}; } // expected-error {{narrowing conversion}} { A<3> a{1, 2, 3, 4, 5, 6, 7, 8}; } { A<3> a = {1, 2, 3, 4, 5, 6, 7, 8}; } { A<3> a{1, 2, 3, 4, 5, 6, 7, 8}; } { A<3> a{1, 2}; } + + { D<0> d{1, 2, 3}; } + { D<1> d{1.0, 2.0, 3.0}; } + { D<-1> d{1, 2.0}; } // expected-error {{ambiguous}} + + { E<0> e{1, 2}; } + } + + void explicit_implicit() { + { X1 x{0}; } + { X1 x = {0}; } + { X2 x{0}; } + { X2 x = {0}; } // expected-error {{explicit}} } struct C { @@ -172,3 +201,28 @@ namespace litb { B g({1, 2, 3}); } + +namespace aggregate { + // Direct list initialization does NOT allow braces to be elided! + struct S { + int ar[2]; + struct T { + int i1; + int i2; + } t; + struct U { + int i1; + } u[2]; + struct V { + int var[2]; + } v; + }; + + void test() { + S s1 = { 1, 2, 3 ,4, 5, 6, 7, 8 }; // no-error + S s2{ {1, 2}, {3, 4}, { {5}, {6} }, { {7, 8} } }; // completely braced + S s3{ 1, 2, 3, 4, 5, 6 }; // xpected-error + S s4{ {1, 2}, {3, 4}, {5, 6}, { {7, 8} } }; // xpected-error + S s5{ {1, 2}, {3, 4}, { {5}, {6} }, {7, 8} }; // xpected-error + } +} diff --git a/test/SemaCXX/i-c-e-cxx.cpp b/test/SemaCXX/i-c-e-cxx.cpp index 2d08ea9a428f..186e32126a39 100644 --- a/test/SemaCXX/i-c-e-cxx.cpp +++ b/test/SemaCXX/i-c-e-cxx.cpp @@ -42,3 +42,16 @@ namespace pr6206 { void pr6373(const unsigned x = 0) { unsigned max = 80 / x; } + + +// rdar://9204520 +namespace rdar9204520 { + +struct A { + static const int B = int(0.75 * 1000 * 1000); +}; + +int foo() { return A::B; } +} + + diff --git a/test/SemaCXX/member-init.cpp b/test/SemaCXX/member-init.cpp index 1b8c523e4586..7ca1f0e4513e 100644 --- a/test/SemaCXX/member-init.cpp +++ b/test/SemaCXX/member-init.cpp @@ -48,3 +48,7 @@ struct CheckExcSpecFail { CheckExcSpecFail() noexcept(true) = default; // expected-error {{exception specification of explicitly defaulted default constructor does not match the calculated one}} ThrowCtor tc = 123; }; + +struct TypedefInit { + typedef int A = 0; // expected-error {{illegal initializer}} +}; diff --git a/test/SemaCXX/member-pointer.cpp b/test/SemaCXX/member-pointer.cpp index de3b211acf50..cf6481015a82 100644 --- a/test/SemaCXX/member-pointer.cpp +++ b/test/SemaCXX/member-pointer.cpp @@ -296,3 +296,19 @@ namespace PR9973 { mem_fn(&test::nullary_v)(t); // expected-note{{in instantiation of}} } } + +namespace test8 { + struct A { int foo; }; + int test1() { + // Verify that we perform (and check) an lvalue conversion on the operands here. + return (*((A**) 0)) // expected-warning {{indirection of non-volatile null pointer will be deleted}} expected-note {{consider}} + ->**(int A::**) 0; // expected-warning {{indirection of non-volatile null pointer will be deleted}} expected-note {{consider}} + } + + int test2() { + // Verify that we perform (and check) an lvalue conversion on the operands here. + // TODO: the .* should itself warn about being a dereference of null. + return (*((A*) 0)) + .**(int A::**) 0; // expected-warning {{indirection of non-volatile null pointer will be deleted}} expected-note {{consider}} + } +} diff --git a/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp new file mode 100644 index 000000000000..fd0c976bdd78 --- /dev/null +++ b/test/SemaCXX/missing-namespace-qualifier-typo-corrections.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-c++0x-extensions %s + +namespace fizbin { class Foobar; } // expected-note{{'fizbin::Foobar' declared here}} +Foobar *my_bar = new Foobar; // expected-error{{unknown type name 'Foobar'; did you mean 'fizbin::Foobar'?}} \ + // expected-error{{expected a type}} + +namespace barstool { int toFoobar() { return 1; } } // expected-note 3 {{'barstool::toFoobar' declared here}} +int Double(int x) { return x + x; } +void empty() { + Double(toFoobar()); // expected-error{{{use of undeclared identifier 'toFoobar'; did you mean 'barstool::toFoobar'?}} +} + +namespace fizbin { + namespace baztool { bool toFoobar() { return true; } } // expected-note{{'fizbin::baztool' declared here}} + namespace nested { bool moreFoobar() { return true; } } // expected-note{{'fizbin::nested::moreFoobar' declared here}} + namespace nested { bool lessFoobar() { return true; } } // expected-note{{'fizbin::nested' declared here}} \ + // expected-note{{'fizbin::nested::lessFoobar' declared here}} + class dummy { // expected-note 2 {{'fizbin::dummy' declared here}} + public: + static bool moreFoobar() { return false; } // expected-note{{'moreFoobar' declared here}} + }; +} +void Check() { // expected-note{{'Check' declared here}} + if (toFoobar()) Double(7); // expected-error{{use of undeclared identifier 'toFoobar'; did you mean 'barstool::toFoobar'?}} + if (noFoobar()) Double(7); // expected-error{{use of undeclared identifier 'noFoobar'; did you mean 'barstool::toFoobar'?}} + if (moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'moreFoobar'; did you mean 'fizbin::nested::moreFoobar'}} + if (lessFoobar()) Double(7); // expected-error{{use of undeclared identifier 'lessFoobar'; did you mean 'fizbin::nested::lessFoobar'?}} + if (baztool::toFoobar()) Double(7); // expected-error{{use of undeclared identifier 'baztool'; did you mean 'fizbin::baztool'?}} + if (nested::moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'nested'; did you mean 'fizbin::nested'?}} + if (dummy::moreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} + if (dummy::mreFoobar()) Double(7); // expected-error{{use of undeclared identifier 'dummy'; did you mean 'fizbin::dummy'?}} \ + // expected-error{{no member named 'mreFoobar' in 'fizbin::dummy'; did you mean 'moreFoobar'?}} + if (moFoobin()) Double(7); // expected-error{{use of undeclared identifier 'moFoobin'}} +} + +void Alt() { + Cleck(); // expected-error{{{use of undeclared identifier 'Cleck'; did you mean 'Check'?}} +} + +namespace N { + namespace inner { + class myvector { /* ... */ }; // expected-note{{'inner::myvector' declared here}} + } + + void f() { + myvector v; // expected-error{{unknown type name 'myvector'; did you mean 'inner::myvector'?}} + } +} + +namespace realstd { + inline namespace __1 { + class mylinkedlist { /* ... */ }; // expected-note 2 {{'realstd::mylinkedlist' declared here}} + } + + class linkedlist { /* ... */ }; +} + +void f() { + mylinkedlist v; // expected-error{{unknown type name 'mylinkedlist'; did you mean 'realstd::mylinkedlist'?}} + nylinkedlist w; // expected-error{{unknown type name 'nylinkedlist'; did you mean 'realstd::mylinkedlist'?}} +} + +// Test case from http://llvm.org/bugs/show_bug.cgi?id=10318 +namespace llvm { + template <typename T> class GraphWriter {}; // expected-note{{'llvm::GraphWriter' declared here}} +} + +struct S {}; +void bar() { + GraphWriter<S> x; //expected-error{{no template named 'GraphWriter'; did you mean 'llvm::GraphWriter'?}} + +} diff --git a/test/SemaCXX/nested-name-spec.cpp b/test/SemaCXX/nested-name-spec.cpp index fef70931717e..ee6ca8857374 100644 --- a/test/SemaCXX/nested-name-spec.cpp +++ b/test/SemaCXX/nested-name-spec.cpp @@ -261,8 +261,27 @@ namespace PR8159 { namespace rdar7980179 { class A { void f0(); }; // expected-note {{previous}} - int A::f0() {} // expected-error {{out-of-line definition of 'rdar7980179::A::f0' differ from the declaration in the return type}} + int A::f0() {} // expected-error {{out-of-line definition of 'rdar7980179::A::f0' differs from the declaration in the return type}} } namespace alias = A; double *dp = (alias::C*)0; // expected-error{{cannot initialize a variable of type 'double *' with an rvalue of type 'alias::C *'}} + +// http://llvm.org/PR10109 +namespace PR10109 { +template<typename T> +struct A { +protected: + struct B; + struct B::C; // expected-error {{requires a template parameter list}} \ + // expected-error {{no struct named 'C'}} +}; + +template<typename T> +struct A2 { +protected: + struct B; +}; +template <typename T> +struct A2<T>::B::C; // expected-error {{no struct named 'C'}} +} diff --git a/test/SemaCXX/new-delete.cpp b/test/SemaCXX/new-delete.cpp index 13ef461e7c0d..efdfa0f066db 100644 --- a/test/SemaCXX/new-delete.cpp +++ b/test/SemaCXX/new-delete.cpp @@ -389,3 +389,11 @@ namespace PR7702 { new DoesNotExist; // expected-error {{expected a type}} } } + +namespace ArrayNewNeedsDtor { + struct A { A(); private: ~A(); }; // expected-note {{declared private here}} + struct B { B(); A a; }; // expected-error {{field of type 'ArrayNewNeedsDtor::A' has private destructor}} + B *test9() { + return new B[5]; // expected-note {{implicit default destructor for 'ArrayNewNeedsDtor::B' first required here}} + } +} diff --git a/test/SemaCXX/null_in_arithmetic_ops.cpp b/test/SemaCXX/null_in_arithmetic_ops.cpp new file mode 100644 index 000000000000..fab6f10ab785 --- /dev/null +++ b/test/SemaCXX/null_in_arithmetic_ops.cpp @@ -0,0 +1,93 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fsyntax-only -fblocks -Wnull-arithmetic -verify %s +#include <stddef.h> + +void f() { + int a; + bool b; + void (^c)(); + class X; + void (X::*d) (); + extern void e(); + int f[2]; + const void *v; + + a = 0 ? NULL + a : a + NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL - a : a - NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL / a : a / NULL; // expected-warning 2{{use of NULL in arithmetic operation}} \ + // expected-warning {{division by zero is undefined}} + a = 0 ? NULL * a : a * NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL >> a : a >> NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL << a : a << NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL % a : a % NULL; // expected-warning 2{{use of NULL in arithmetic operation}} \ + expected-warning {{remainder by zero is undefined}} + a = 0 ? NULL & a : a & NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL | a : a | NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + a = 0 ? NULL ^ a : a ^ NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + + // Check for warnings or errors when doing arithmetic on pointers and other + // types. + v = 0 ? NULL + &a : &a + NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + v = 0 ? NULL + c : c + NULL; // \ + expected-error {{invalid operands to binary expression ('long' and 'void (^)()')}} \ + expected-error {{invalid operands to binary expression ('void (^)()' and 'long')}} + v = 0 ? NULL + d : d + NULL; // \ + expected-error {{invalid operands to binary expression ('long' and 'void (X::*)()')}} \ + expected-error {{invalid operands to binary expression ('void (X::*)()' and 'long')}} + v = 0 ? NULL + e : e + NULL; // expected-error 2{{arithmetic on a pointer to the function type 'void ()'}} + v = 0 ? NULL + f : f + NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + v = 0 ? NULL + "f" : "f" + NULL; // expected-warning 2{{use of NULL in arithmetic operation}} + + // Using two NULLs should only give one error instead of two. + a = NULL + NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL - NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL / NULL; // expected-warning{{use of NULL in arithmetic operation}} \ + // expected-warning{{division by zero is undefined}} + a = NULL * NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL >> NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL << NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL % NULL; // expected-warning{{use of NULL in arithmetic operation}} \ + // expected-warning{{remainder by zero is undefined}} + a = NULL & NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL | NULL; // expected-warning{{use of NULL in arithmetic operation}} + a = NULL ^ NULL; // expected-warning{{use of NULL in arithmetic operation}} + + a += NULL; // expected-warning{{use of NULL in arithmetic operation}} + a -= NULL; // expected-warning{{use of NULL in arithmetic operation}} + a /= NULL; // expected-warning{{use of NULL in arithmetic operation}} \ + // expected-warning{{division by zero is undefined}} + a *= NULL; // expected-warning{{use of NULL in arithmetic operation}} + a >>= NULL; // expected-warning{{use of NULL in arithmetic operation}} + a <<= NULL; // expected-warning{{use of NULL in arithmetic operation}} + a %= NULL; // expected-warning{{use of NULL in arithmetic operation}} \ + // expected-warning{{remainder by zero is undefined}} + a &= NULL; // expected-warning{{use of NULL in arithmetic operation}} + a |= NULL; // expected-warning{{use of NULL in arithmetic operation}} + a ^= NULL; // expected-warning{{use of NULL in arithmetic operation}} + + b = a < NULL || NULL < a; // expected-warning 2{{use of NULL in arithmetic operation}} + b = a > NULL || NULL > a; // expected-warning 2{{use of NULL in arithmetic operation}} + b = a <= NULL || NULL <= a; // expected-warning 2{{use of NULL in arithmetic operation}} + b = a >= NULL || NULL >= a; // expected-warning 2{{use of NULL in arithmetic operation}} + b = a == NULL || NULL == a; // expected-warning 2{{use of NULL in arithmetic operation}} + b = a != NULL || NULL != a; // expected-warning 2{{use of NULL in arithmetic operation}} + + b = &a < NULL || NULL < &a || &a > NULL || NULL > &a; + b = &a <= NULL || NULL <= &a || &a >= NULL || NULL >= &a; + b = &a == NULL || NULL == &a || &a != NULL || NULL != &a; + + b = 0 == a; + b = 0 == &a; + + b = NULL < NULL || NULL > NULL; + b = NULL <= NULL || NULL >= NULL; + b = NULL == NULL || NULL != NULL; + + b = ((NULL)) != a; // expected-warning{{use of NULL in arithmetic operation}} + + // Check that even non-standard pointers don't warn. + b = c == NULL || NULL == c || c != NULL || NULL != c; + b = d == NULL || NULL == d || d != NULL || NULL != d; + b = e == NULL || NULL == e || e != NULL || NULL != e; + b = f == NULL || NULL == f || f != NULL || NULL != f; + b = "f" == NULL || NULL == "f" || "f" != NULL || NULL != "f"; +} diff --git a/test/SemaCXX/nullptr_in_arithmetic_ops.cpp b/test/SemaCXX/nullptr_in_arithmetic_ops.cpp new file mode 100644 index 000000000000..e839ed116f8d --- /dev/null +++ b/test/SemaCXX/nullptr_in_arithmetic_ops.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -fsyntax-only -fblocks -std=c++0x -verify %s + +void foo() { + int a; + bool b; + + a = 0 ? nullptr + a : a + nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr - a : a - nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr / a : a / nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr * a : a * nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr >> a : a >> nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr << a : a << nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr % a : a % nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr & a : a & nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr | a : a | nullptr; // expected-error 2{{invalid operands to binary expression}} + a = 0 ? nullptr ^ a : a ^ nullptr; // expected-error 2{{invalid operands to binary expression}} + + // Using two nullptrs should only give one error instead of two. + a = nullptr + nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr - nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr / nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr * nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr >> nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr << nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr % nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr & nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr | nullptr; // expected-error{{invalid operands to binary expression}} + a = nullptr ^ nullptr; // expected-error{{invalid operands to binary expression}} + + a += nullptr; // expected-error{{invalid operands to binary expression}} + a -= nullptr; // expected-error{{invalid operands to binary expression}} + a /= nullptr; // expected-error{{invalid operands to binary expression}} + a *= nullptr; // expected-error{{invalid operands to binary expression}} + a >>= nullptr; // expected-error{{invalid operands to binary expression}} + a <<= nullptr; // expected-error{{invalid operands to binary expression}} + a %= nullptr; // expected-error{{invalid operands to binary expression}} + a &= nullptr; // expected-error{{invalid operands to binary expression}} + a |= nullptr; // expected-error{{invalid operands to binary expression}} + a ^= nullptr; // expected-error{{invalid operands to binary expression}} + + b = a < nullptr || nullptr < a; // expected-error 2{{invalid operands to binary expression}} + b = a > nullptr || nullptr > a; // expected-error 2{{invalid operands to binary expression}} + b = a <= nullptr || nullptr <= a; // expected-error 2{{invalid operands to binary expression}} + b = a >= nullptr || nullptr >= a; // expected-error 2{{invalid operands to binary expression}} + b = a == nullptr || nullptr == a; // expected-error 2{{invalid operands to binary expression}} + b = a != nullptr || nullptr != a; // expected-error 2{{invalid operands to binary expression}} + + b = &a < nullptr || nullptr < &a || &a > nullptr || nullptr > &a; + b = &a <= nullptr || nullptr <= &a || &a >= nullptr || nullptr >= &a; + b = &a == nullptr || nullptr == &a || &a != nullptr || nullptr != &a; + + b = nullptr < nullptr || nullptr > nullptr; + b = nullptr <= nullptr || nullptr >= nullptr; + b = nullptr == nullptr || nullptr != nullptr; + + b = ((nullptr)) != a; // expected-error{{invalid operands to binary expression}} + + void (^c)(); + c = nullptr; + b = c == nullptr || nullptr == c || c != nullptr || nullptr != c; + + class X; + void (X::*d) (); + d = nullptr; + b = d == nullptr || nullptr == d || d != nullptr || nullptr != d; + + extern void e(); + b = e == nullptr || nullptr == e || e != nullptr || nullptr != e; + + int f[2]; + b = f == nullptr || nullptr == f || f != nullptr || nullptr != f; + b = "f" == nullptr || nullptr == "f" || "f" != nullptr || nullptr != "f"; +} diff --git a/test/SemaCXX/overloaded-name.cpp b/test/SemaCXX/overloaded-name.cpp index 5a87cc8e67e6..73f12a902723 100644 --- a/test/SemaCXX/overloaded-name.cpp +++ b/test/SemaCXX/overloaded-name.cpp @@ -12,3 +12,19 @@ void test(bool b) { (void)(b? ovl<float> : &ovl); // expected-error{{cannot resolve overloaded function 'ovl' from context}} (void)(b? ovl<float> : ovl<float>); } + +namespace rdar9623945 { + void f(...) { + } + + class X { + public: + const char* text(void); + void g(void) { + f(text()); + f(text); // expected-error{{a bound member function may only be called}} + f(text()); + f(text); // expected-error{{a bound member function may only be called}} + } + }; +} diff --git a/test/SemaCXX/return.cpp b/test/SemaCXX/return.cpp index b457f6ace55a..2f98a277f638 100644 --- a/test/SemaCXX/return.cpp +++ b/test/SemaCXX/return.cpp @@ -69,3 +69,14 @@ namespace PR10057 { return S() = value; } } + +namespace return_has_expr { + struct S { + S() { + return 42; // expected-error {{constructor 'S' should not return a value}} + } + ~S() { + return 42; // expected-error {{destructor '~S' should not return a value}} + } + }; +} diff --git a/test/SemaCXX/vararg-non-pod.cpp b/test/SemaCXX/vararg-non-pod.cpp index df0080fac94a..3ca07b0215c3 100644 --- a/test/SemaCXX/vararg-non-pod.cpp +++ b/test/SemaCXX/vararg-non-pod.cpp @@ -101,3 +101,21 @@ void t6(Foo somearg, ... ) { __builtin_va_start(list, somearg); } +void t7(int n, ...) { + __builtin_va_list list; + __builtin_va_start(list, n); + (void)__builtin_va_arg(list, C); // expected-warning{{second argument to 'va_arg' is of non-POD type 'C'}} + __builtin_va_end(list); +} + +struct Abstract { + virtual void doit() = 0; // expected-note{{unimplemented pure virtual method}} +}; + +void t8(int n, ...) { + __builtin_va_list list; + __builtin_va_start(list, n); + (void)__builtin_va_arg(list, Abstract); // expected-error{{second argument to 'va_arg' is of abstract type 'Abstract'}} + __builtin_va_end(list); +} + diff --git a/test/SemaCXX/vector.cpp b/test/SemaCXX/vector.cpp index 9ae2c82fc530..4d3d93974c16 100644 --- a/test/SemaCXX/vector.cpp +++ b/test/SemaCXX/vector.cpp @@ -176,10 +176,10 @@ void test_implicit_conversions(bool Cond, char16 c16, longlong16 ll16, (void)(to_c16 * to_c16e); (void)(to_c16 / to_c16e); (void)(rto_c16 = c16e); // expected-error{{no viable overloaded '='}} - (void)(rto_c16 += to_c16e); // expected-error{{expression is not assignable}} - (void)(rto_c16 -= to_c16e); // expected-error{{expression is not assignable}} - (void)(rto_c16 *= to_c16e); // expected-error{{expression is not assignable}} - (void)(rto_c16 /= to_c16e); // expected-error{{expression is not assignable}} + (void)(rto_c16 += to_c16e); + (void)(rto_c16 -= to_c16e); + (void)(rto_c16 *= to_c16e); + (void)(rto_c16 /= to_c16e); (void)(Cond? to_c16 : to_c16e); (void)(Cond? to_ll16e : to_ll16); diff --git a/test/SemaCXX/virtuals.cpp b/test/SemaCXX/virtuals.cpp index 792467e5f86b..d8c26efcfb42 100644 --- a/test/SemaCXX/virtuals.cpp +++ b/test/SemaCXX/virtuals.cpp @@ -1,8 +1,8 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -fcxx-exceptions -verify -std=c++0x %s class A { virtual void f(); - virtual void g() = 0; + virtual void g() = 0; // expected-note{{unimplemented pure virtual method 'g' in 'A'}} void h() = 0; // expected-error {{'h' is not virtual and cannot be declared pure}} void i() = 1; // expected-error {{initializer on function does not look like a pure-specifier}} @@ -19,20 +19,29 @@ virtual void A::k() { } // expected-error{{'virtual' can only be specified insid class B : public A { // Needs to recognize that overridden function is virtual. - //void g() = 0; + void g() = 0; // Needs to recognize that function does not override. - //void g(int) = 0; + void g(int) = 0; // expected-error{{'g' is not virtual and cannot be declared pure}} }; // Needs to recognize invalid uses of abstract classes. -/* -A fn(A) +A fn(A) // expected-error{{parameter type 'A' is an abstract class}} \ + // expected-error{{return type 'A' is an abstract class}} { - A a; - static_cast<A>(0); + A a; // expected-error{{variable type 'A' is an abstract class}} + (void)static_cast<A>(0); try { - } catch(A) { + } catch(A) { // expected-error{{variable type 'A' is an abstract class}} } } -*/ + +namespace rdar9670557 { + typedef int func(int); + func *a(); + struct X { + virtual func f = 0; + virtual func (g) = 0; + func *h = 0; + }; +} diff --git a/test/SemaCXX/warn-bad-memaccess.cpp b/test/SemaCXX/warn-bad-memaccess.cpp index e7d095f0edce..9a998f020cb4 100644 --- a/test/SemaCXX/warn-bad-memaccess.cpp +++ b/test/SemaCXX/warn-bad-memaccess.cpp @@ -38,6 +38,46 @@ void test_warn() { memcpy(0, &x1, sizeof x1); // \ // expected-warning{{source of this 'memcpy' call is a pointer to dynamic class}} \ // expected-note {{explicitly cast the pointer to silence this warning}} + + __builtin_memset(&x1, 0, sizeof x1); // \ + // expected-warning {{destination for this '__builtin_memset' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin_memset(&x2, 0, sizeof x2); // \ + // expected-warning {{destination for this '__builtin_memset' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + + __builtin_memmove(&x1, 0, sizeof x1); // \ + // expected-warning{{destination for this '__builtin_memmove' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin_memmove(0, &x1, sizeof x1); // \ + // expected-warning{{source of this '__builtin_memmove' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin_memcpy(&x1, 0, sizeof x1); // \ + // expected-warning{{destination for this '__builtin_memcpy' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin_memcpy(0, &x1, sizeof x1); // \ + // expected-warning{{source of this '__builtin_memcpy' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + + __builtin___memset_chk(&x1, 0, sizeof x1, sizeof x1); // \ + // expected-warning {{destination for this '__builtin___memset_chk' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin___memset_chk(&x2, 0, sizeof x2, sizeof x2); // \ + // expected-warning {{destination for this '__builtin___memset_chk' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + + __builtin___memmove_chk(&x1, 0, sizeof x1, sizeof x1); // \ + // expected-warning{{destination for this '__builtin___memmove_chk' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin___memmove_chk(0, &x1, sizeof x1, sizeof x1); // \ + // expected-warning{{source of this '__builtin___memmove_chk' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin___memcpy_chk(&x1, 0, sizeof x1, sizeof x1); // \ + // expected-warning{{destination for this '__builtin___memcpy_chk' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} + __builtin___memcpy_chk(0, &x1, sizeof x1, sizeof x1); // \ + // expected-warning{{source of this '__builtin___memcpy_chk' call is a pointer to dynamic class}} \ + // expected-note {{explicitly cast the pointer to silence this warning}} } void test_nowarn(void *void_ptr) { @@ -68,3 +108,5 @@ namespace N { N::memset(&x1, 0, sizeof x1); } } + + diff --git a/test/SemaCXX/warn-memset-bad-sizeof.cpp b/test/SemaCXX/warn-memset-bad-sizeof.cpp new file mode 100644 index 000000000000..90ac50472e98 --- /dev/null +++ b/test/SemaCXX/warn-memset-bad-sizeof.cpp @@ -0,0 +1,101 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wno-sizeof-array-argument %s +// +extern "C" void *memset(void *, int, unsigned); +extern "C" void *memmove(void *s1, const void *s2, unsigned n); +extern "C" void *memcpy(void *s1, const void *s2, unsigned n); + +struct S {int a, b, c, d;}; +typedef S* PS; + +struct Foo {}; +typedef const Foo& CFooRef; +typedef const Foo CFoo; +typedef volatile Foo VFoo; +typedef const volatile Foo CVFoo; + +typedef double Mat[4][4]; + +template <class Dest, class Source> +inline Dest bit_cast(const Source& source) { + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// http://www.lysator.liu.se/c/c-faq/c-2.html#2-6 +void f(Mat m, const Foo& const_foo, char *buffer) { + S s; + S* ps = &s; + PS ps2 = &s; + char arr[5]; + char* parr[5]; + Foo foo; + char* heap_buffer = new char[42]; + + /* Should warn */ + memset(&s, 0, sizeof(&s)); // \ + // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}} + memset(ps, 0, sizeof(ps)); // \ + // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}} + memset(ps2, 0, sizeof(ps2)); // \ + // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}} + memset(ps2, 0, sizeof(typeof(ps2))); // \ + // expected-warning {{argument to 'sizeof' in 'memset' call is the same pointer type}} + memset(ps2, 0, sizeof(PS)); // \ + // expected-warning {{argument to 'sizeof' in 'memset' call is the same pointer type}} + memset(heap_buffer, 0, sizeof(heap_buffer)); // \ + // expected-warning {{argument to 'sizeof' in 'memset' call is the same expression as the destination}} + + memcpy(&s, 0, sizeof(&s)); // \ + // expected-warning {{argument to 'sizeof' in 'memcpy' call is the same expression as the destination}} + memcpy(0, &s, sizeof(&s)); // \ + // expected-warning {{argument to 'sizeof' in 'memcpy' call is the same expression as the source}} + + /* Shouldn't warn */ + memset((void*)&s, 0, sizeof(&s)); + memset(&s, 0, sizeof(s)); + memset(&s, 0, sizeof(S)); + memset(&s, 0, sizeof(const S)); + memset(&s, 0, sizeof(volatile S)); + memset(&s, 0, sizeof(volatile const S)); + memset(&foo, 0, sizeof(CFoo)); + memset(&foo, 0, sizeof(VFoo)); + memset(&foo, 0, sizeof(CVFoo)); + memset(ps, 0, sizeof(*ps)); + memset(ps2, 0, sizeof(*ps2)); + memset(ps2, 0, sizeof(typeof(*ps2))); + memset(arr, 0, sizeof(arr)); + memset(parr, 0, sizeof(parr)); + + memcpy(&foo, &const_foo, sizeof(Foo)); + memcpy((void*)&s, 0, sizeof(&s)); + memcpy(0, (void*)&s, sizeof(&s)); + char *cptr; + memcpy(&cptr, buffer, sizeof(cptr)); + memcpy((char*)&cptr, buffer, sizeof(cptr)); + + CFooRef cfoo = foo; + memcpy(&foo, &cfoo, sizeof(Foo)); + + memcpy(0, &arr, sizeof(arr)); + typedef char Buff[8]; + memcpy(0, &arr, sizeof(Buff)); + + unsigned char* puc; + bit_cast<char*>(puc); + + float* pf; + bit_cast<int*>(pf); + + int iarr[14]; + memset(&iarr[0], 0, sizeof iarr); + + int* iparr[14]; + memset(&iparr[0], 0, sizeof iparr); + + memset(m, 0, sizeof(Mat)); + + // Copy to raw buffer shouldn't warn either + memcpy(&foo, &arr, sizeof(Foo)); + memcpy(&arr, &foo, sizeof(Foo)); +} diff --git a/test/SemaCXX/warn-pure-virtual-call-from-ctor-dtor.cpp b/test/SemaCXX/warn-pure-virtual-call-from-ctor-dtor.cpp index 698eccd1d2f0..ade6198fbd0d 100644 --- a/test/SemaCXX/warn-pure-virtual-call-from-ctor-dtor.cpp +++ b/test/SemaCXX/warn-pure-virtual-call-from-ctor-dtor.cpp @@ -5,3 +5,10 @@ struct A { virtual void f() = 0; // expected-note 2 {{'f' declared here}} }; + +// Don't warn (or note) when calling the function on a pointer. (PR10195) +struct B { + A *a; + B() { a->f(); }; + ~B() { a->f(); }; +}; diff --git a/test/SemaCXX/warn-unused-variables.cpp b/test/SemaCXX/warn-unused-variables.cpp index 81f22a796a0e..5ba1f2a5f34b 100644 --- a/test/SemaCXX/warn-unused-variables.cpp +++ b/test/SemaCXX/warn-unused-variables.cpp @@ -54,3 +54,29 @@ void unused_local_static() { static int y = 0; // expected-warning{{unused variable 'y'}} #pragma unused(x) } + +// PR10168 +namespace PR10168 { + // We expect a warning in the definition only for non-dependent variables, and + // a warning in the instantiation only for dependent variables. + template<typename T> + struct S { + void f() { + int a; // expected-warning {{unused variable 'a'}} + T b; // expected-warning 2{{unused variable 'b'}} + } + }; + + template<typename T> + void f() { + int a; // expected-warning {{unused variable 'a'}} + T b; // expected-warning 2{{unused variable 'b'}} + } + + void g() { + S<int>().f(); // expected-note {{here}} + S<char>().f(); // expected-note {{here}} + f<int>(); // expected-note {{here}} + f<char>(); // expected-note {{here}} + } +} diff --git a/test/SemaObjC/Inputs/arc-system-header.h b/test/SemaObjC/Inputs/arc-system-header.h new file mode 100644 index 000000000000..5012a2a37837 --- /dev/null +++ b/test/SemaObjC/Inputs/arc-system-header.h @@ -0,0 +1,52 @@ +static inline void *test0(id x) { + return x; +} + +static inline void **test1(__strong id* x) { + return (void**) x; +} + + + + + +struct Test3 { + id *field; +}; + +@interface Test4 { +@public + id *field1; + __strong id *field2; +} +@end + +struct Test5 { + id field; +}; + + + + + + + +extern struct Test6 *const kMagicConstant; + + + + + +@interface Test7 +@property id *prop; +@end + + + + + + + +static inline void *test8(id ptr) { + return (__bridge_retain void*) ptr; +} diff --git a/test/SemaObjC/arc-bridged-cast.m b/test/SemaObjC/arc-bridged-cast.m new file mode 100644 index 000000000000..e883406db908 --- /dev/null +++ b/test/SemaObjC/arc-bridged-cast.m @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fblocks %s + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +void from_cf() { + id obj1 = (__bridge_transfer id)CFCreateSomething(); + id obj2 = (__bridge_transfer NSString*)CFCreateString(); + (__bridge int*)CFCreateSomething(); // expected-error{{incompatible types casting 'CFTypeRef' (aka 'const void *') to 'int *' with a __bridge cast}} + id obj3 = (__bridge id)CFGetSomething(); + id obj4 = (__bridge NSString*)CFGetString(); +} + +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained CFTypeRef)CreateSomething(); + CFStringRef cf2 = (__bridge_retained CFStringRef)CreateNSString(); + CFTypeRef cf3 = (__bridge CFTypeRef)CreateSomething(); + CFStringRef cf4 = (__bridge CFStringRef)CreateNSString(); + + // rdar://problem/9629566 - temporary workaround + CFTypeRef cf5 = (__bridge_retain CFTypeRef)CreateSomething(); // expected-error {{unknown cast annotation __bridge_retain; did you mean __bridge_retained?}} +} + +void fixits() { + id obj1 = (id)CFCreateSomething(); // expected-error{{cast of C pointer type 'CFTypeRef' (aka 'const void *') to Objective-C pointer type 'id' requires a bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'CFTypeRef' (aka 'const void *') into ARC}} + CFTypeRef cf1 = (CFTypeRef)CreateSomething(); // expected-error{{cast of Objective-C pointer type 'id' to C pointer type 'CFTypeRef' (aka 'const void *') requires a bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'CFTypeRef' (aka 'const void *')}} +} diff --git a/test/SemaObjC/arc-decls.m b/test/SemaObjC/arc-decls.m new file mode 100644 index 000000000000..e713d239a0cf --- /dev/null +++ b/test/SemaObjC/arc-decls.m @@ -0,0 +1,64 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-nonfragile-abi -verify %s + +// rdar://8843524 + +struct A { + id x; // expected-error {{ARC forbids Objective-C objects in structs or unions}} +}; + +union u { + id u; // expected-error {{ARC forbids Objective-C objects in structs or unions}} +}; + +@interface I { + struct A a; + struct B { + id y[10][20]; // expected-error {{ARC forbids Objective-C objects in structs or unions}} + id z; + } b; + + union u c; +}; +@end + +struct S { + id __attribute__((objc_ownership(none))) i; + void * vp; + int i1; +}; + +// rdar://9046528 + +@class NSError; + +__autoreleasing id X; // expected-error {{global variables cannot have __autoreleasing ownership}} +__autoreleasing NSError *E; // expected-error {{global variables cannot have __autoreleasing ownership}} + + +extern id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing ownership}} + +void func() +{ + id X; + static id __autoreleasing X1; // expected-error {{global variables cannot have __autoreleasing ownership}} + extern id __autoreleasing E; // expected-error {{global variables cannot have __autoreleasing ownership}} + +} + +// rdar://9157348 + +@interface J +@property (retain) id newFoo; // expected-note {{property declared here}} +@property (strong) id copyBar; // expected-note {{property declared here}} +@property (copy) id allocBaz; // expected-note {{property declared here}} +@property (copy, nonatomic) id new; +@end + +@implementation J +@synthesize newFoo; // expected-error {{property's synthesized getter follows Cocoa naming convention for returning}} +@synthesize copyBar; // expected-error {{property's synthesized getter follows Cocoa naming convention for returning}} +@synthesize allocBaz; // expected-error {{property's synthesized getter follows Cocoa naming convention for returning}} +@synthesize new; +- new {return 0; }; +@end + diff --git a/test/SemaObjC/arc-jump-block.m b/test/SemaObjC/arc-jump-block.m new file mode 100644 index 000000000000..1c7b21e4e8cd --- /dev/null +++ b/test/SemaObjC/arc-jump-block.m @@ -0,0 +1,84 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s +// rdar://9535237 + +typedef struct dispatch_queue_s *dispatch_queue_t; + +typedef void (^dispatch_block_t)(void); + +void dispatch_async(dispatch_queue_t queue, dispatch_block_t block); + +extern __attribute__((visibility("default"))) struct dispatch_queue_s _dispatch_main_q; + +@interface SwitchBlockCrashAppDelegate +- (void)pageLeft; +- (void)pageRight;; +@end + +@implementation SwitchBlockCrashAppDelegate + +- (void)choose:(int)button { + switch (button) { + case 0: + dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; }); // expected-note 3 {{jump enters lifetime of block which strongly captures a variable}} + break; + case 2: // expected-error {{switch case is in protected scope}} + dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; }); // expected-note 2 {{jump enters lifetime of block which strongly captures a variable}} + break; + case 3: // expected-error {{switch case is in protected scope}} + { + dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; }); + break; + } + case 4: // expected-error {{switch case is in protected scope}} + break; + } + + __block SwitchBlockCrashAppDelegate *captured_block_obj; + switch (button) { + case 10: + { + dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; }); + break; + } + case 12: + if (button) + dispatch_async((&_dispatch_main_q), ^{ [captured_block_obj pageRight]; }); + break; + case 13: + while (button) + dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; }); + break; + case 14: + break; + } + + switch (button) { + case 10: + { + dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; }); + break; + } + case 12: + if (button) + dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; }); + switch (button) { + case 0: + { + dispatch_async((&_dispatch_main_q), ^{ [self pageLeft]; }); + break; + } + case 4: + break; + } + break; + case 13: + while (button) + dispatch_async((&_dispatch_main_q), ^{ [self pageRight]; }); + break; + case 14: + break; + } +} +- (void)pageLeft {} +- (void)pageRight {} +@end diff --git a/test/SemaObjC/arc-no-runtime.m b/test/SemaObjC/arc-no-runtime.m new file mode 100644 index 000000000000..94299e235e81 --- /dev/null +++ b/test/SemaObjC/arc-no-runtime.m @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -verify %s + +// rdar://problem/9150784 +void test(void) { + __weak id x; // expected-error {{the current deployment target does not support automated __weak references}} +} + +@interface A +@property (weak) id testObjectWeakProperty; // expected-note {{declared here}} +@end + +@implementation A +// rdar://9605088 +@synthesize testObjectWeakProperty; // expected-error {{the current deployment target does not support automated __weak references}} +@end diff --git a/test/SemaObjC/arc-non-pod-memaccess.m b/test/SemaObjC/arc-non-pod-memaccess.m new file mode 100644 index 000000000000..c9a77519694d --- /dev/null +++ b/test/SemaObjC/arc-non-pod-memaccess.m @@ -0,0 +1,55 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s +// RUN: %clang_cc1 -x objective-c++ -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fobjc-runtime-has-weak -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s + +#ifdef __cplusplus +extern "C" { +#endif + +void *memset(void *, int, __SIZE_TYPE__); +void *memmove(void *s1, const void *s2, __SIZE_TYPE__ n); +void *memcpy(void *s1, const void *s2, __SIZE_TYPE__ n); + +#ifdef __cplusplus +} +#endif + +void test(id __strong *sip, id __weak *wip, id __autoreleasing *aip, + id __unsafe_unretained *uip, void *ptr) { + // All okay. + memset(sip, 0, 17); + memset(wip, 0, 17); + memset(aip, 0, 17); + memset(uip, 0, 17); + + memcpy(sip, ptr, 17); // expected-warning{{destination for this 'memcpy' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(wip, ptr, 17); // expected-warning{{destination for this 'memcpy' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(aip, ptr, 17); // expected-warning{{destination for this 'memcpy' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(uip, ptr, 17); + + memcpy(ptr, sip, 17); // expected-warning{{source of this 'memcpy' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(ptr, wip, 17); // expected-warning{{source of this 'memcpy' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(ptr, aip, 17); // expected-warning{{source of this 'memcpy' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memcpy(ptr, uip, 17); + + memmove(sip, ptr, 17); // expected-warning{{destination for this 'memmove' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(wip, ptr, 17); // expected-warning{{destination for this 'memmove' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(aip, ptr, 17); // expected-warning{{destination for this 'memmove' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(uip, ptr, 17); + + memmove(ptr, sip, 17); // expected-warning{{source of this 'memmove' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(ptr, wip, 17); // expected-warning{{source of this 'memmove' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(ptr, aip, 17); // expected-warning{{source of this 'memmove' call is a pointer to ownership-qualified type}} \ + // expected-note{{explicitly cast the pointer to silence this warning}} + memmove(ptr, uip, 17); +} diff --git a/test/SemaObjC/arc-peformselector.m b/test/SemaObjC/arc-peformselector.m new file mode 100644 index 000000000000..e637f3d76ff4 --- /dev/null +++ b/test/SemaObjC/arc-peformselector.m @@ -0,0 +1,38 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s +// rdar://9659270 + +@interface NSObject +- (id)copy; // expected-note {{method declared here}} +- (id) test __attribute__((ns_returns_retained)); // expected-note {{method declared here}} ++ (id) new ; // expected-note {{method declared here}} +- (id) init __attribute__((ns_returns_not_retained)); +- (id)PlusZero; +- (id)PlusOne __attribute__((ns_returns_retained)); // expected-note {{method declared here}} +@end + +@interface I : NSObject +{ + SEL sel1; +} +- (id)performSelector:(SEL)aSelector; +- (id)performSelector:(SEL)aSelector withObject:(id)object; +- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2; +@end + +@implementation I +- (id) Meth { + return [self performSelector : @selector(copy)]; // expected-error {{performSelector names a selector which retains the object}} + return [self performSelector : @selector(test)]; // expected-error {{performSelector names a selector which retains the object}} + return [self performSelector : @selector(new)]; // expected-error {{performSelector names a selector which retains the object}} + return [self performSelector : @selector(init)]; + return [self performSelector : sel1]; // expected-warning {{performSelector may cause a leak because its selector is unknown}} \ + // expected-note {{used here}} + + return [self performSelector : @selector(PlusZero)]; + return [self performSelector : @selector(PlusOne)]; // expected-error {{performSelector names a selector which retains the object}} +} + +- (id)performSelector:(SEL)aSelector { return 0; } +- (id)performSelector:(SEL)aSelector withObject:(id)object { return 0; } +- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 { return 0; } +@end diff --git a/test/SemaObjC/arc-property-decl-attrs.m b/test/SemaObjC/arc-property-decl-attrs.m new file mode 100644 index 000000000000..0dd74b8ad5ce --- /dev/null +++ b/test/SemaObjC/arc-property-decl-attrs.m @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify %s +// rdar://9340606 + +@interface Foo { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(strong) id x; +@property(strong) id y; +@property(strong) id z; +@end + +@interface Bar { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(retain) id x; +@property(retain) id y; +@property(retain) id z; +@end + +@interface Bas { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(copy) id x; +@property(copy) id y; +@property(copy) id z; +@end + +// Errors should start about here :-) + +@interface Bat +@property(strong) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(strong) __weak id y; // expected-error {{strong property 'y' may not also be declared __weak}} expected-error {{property attributes 'strong' and 'weak' are mutually exclusive}} +@property(strong) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bau +@property(retain) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(retain) __weak id y; // expected-error {{strong property 'y' may not also be declared __weak}} expected-error {{property attributes 'retain' and 'weak' are mutually exclusive}} +@property(retain) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bav +@property(copy) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(copy) __weak id y; // expected-error {{strong property 'y' may not also be declared __weak}} expected-error {{property attributes 'copy' and 'weak' are mutually exclusive}} +@property(copy) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bingo +@property(assign) __unsafe_unretained id x; +@property(assign) __weak id y; // expected-error {{property attributes 'assign' and 'weak' are mutually exclusive}} +@property(assign) __autoreleasing id z; // expected-error {{unsafe_unretained property 'z' may not also be declared __autoreleasing}} +@end + +@interface Batman +@property(unsafe_unretained) __unsafe_unretained id x; +@property(unsafe_unretained) __weak id y; // expected-error {{property attributes 'unsafe_unretained' and 'weak' are mutually exclusive}} +@property(unsafe_unretained) __autoreleasing id z; // expected-error {{unsafe_unretained property 'z' may not also be declared __autoreleasing}} +@end diff --git a/test/SemaObjC/arc-property-lifetime.m b/test/SemaObjC/arc-property-lifetime.m new file mode 100644 index 000000000000..b1c84c75c058 --- /dev/null +++ b/test/SemaObjC/arc-property-lifetime.m @@ -0,0 +1,112 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify %s +// rdar://9340606 + +@interface Foo { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(strong) id x; // expected-note {{property declared here}} +@property(strong) id y; // expected-note {{property declared here}} +@property(strong) id z; +@end + +@implementation Foo +@synthesize x; // expected-error {{existing ivar 'x' for strong property 'x' may not be __unsafe_unretained}} +@synthesize y; // expected-error {{existing ivar 'y' for strong property 'y' may not be __weak}} +@synthesize z; // suppressed +@end + +@interface Bar { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(retain) id x; // expected-note {{property declared here}} +@property(retain) id y; // expected-note {{property declared here}} +@property(retain) id z; +@end + +@implementation Bar +@synthesize x; // expected-error {{existing ivar 'x' for strong property 'x' may not be __unsafe_unretained}} +@synthesize y; // expected-error {{existing ivar 'y' for strong property 'y' may not be __weak}} +@synthesize z; // suppressed +@end + +@interface Bas { +@public + id __unsafe_unretained x; + id __weak y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(copy) id x; // expected-note {{property declared here}} +@property(copy) id y; // expected-note {{property declared here}} +@property(copy) id z; +@end + +@implementation Bas +@synthesize x; // expected-error {{existing ivar 'x' for strong property 'x' may not be __unsafe_unretained}} +@synthesize y; // expected-error {{existing ivar 'y' for strong property 'y' may not be __weak}} +@synthesize z; // suppressed +@end + +@interface Bat +@property(strong) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(strong) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bau +@property(retain) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(retain) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +@interface Bav +@property(copy) __unsafe_unretained id x; // expected-error {{strong property 'x' may not also be declared __unsafe_unretained}} +@property(copy) __autoreleasing id z; // expected-error {{strong property 'z' may not also be declared __autoreleasing}} +@end + +// rdar://9341593 +@interface Gorf { + id __unsafe_unretained x; + id y; +} +@property(assign) id __unsafe_unretained x; +@property(assign) id y; // expected-note {{property declared here}} +@property(assign) id z; +@end + +@implementation Gorf +@synthesize x; +@synthesize y; // expected-error {{existing ivar 'y' for unsafe_unretained property 'y' must be __unsafe_unretained}} +@synthesize z; +@end + +@interface Gorf2 { + id __unsafe_unretained x; + id y; +} +@property(unsafe_unretained) id __unsafe_unretained x; +@property(unsafe_unretained) id y; // expected-note {{property declared here}} +@property(unsafe_unretained) id z; +@end + +@implementation Gorf2 +@synthesize x; +@synthesize y; // expected-error {{existing ivar 'y' for unsafe_unretained property 'y' must be __unsafe_unretained}} +@synthesize z; +@end + +// rdar://9355230 +@interface I { + char _isAutosaving; +} +@property char isAutosaving; + +@end + +@implementation I +@synthesize isAutosaving = _isAutosaving; +@end + diff --git a/test/SemaObjC/arc-property.m b/test/SemaObjC/arc-property.m new file mode 100644 index 000000000000..0651f1808dc9 --- /dev/null +++ b/test/SemaObjC/arc-property.m @@ -0,0 +1,48 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -fblocks -verify %s +// rdar://9309489 + +@interface MyClass { + id __weak myString; + id StrongIvar; + id __weak myString2; + id __weak myString3; + id StrongIvar5; +} +@property (strong) id myString; // expected-note {{property declared here}} +@property (strong) id myString1; +@property (retain) id myString2; // expected-note {{property declared here}} +// +@property (weak) id myString3; +@property (weak) id myString4; +@property __weak id myString5; // expected-note {{property declared here}} +@end + +@implementation MyClass +@synthesize myString; // expected-error {{existing ivar 'myString' for strong property 'myString' may not be __weak}} +@synthesize myString1 = StrongIvar; // OK +@synthesize myString2 = myString2; // expected-error {{existing ivar 'myString2' for strong property 'myString2' may not be __weak}} +// +@synthesize myString3; // OK +@synthesize myString4; // OK +@synthesize myString5 = StrongIvar5; // expected-error {{existing ivar 'StrongIvar5' for __weak property 'myString5' must be __weak}} + +@end + +// rdar://9340692 +@interface Foo { +@public + id __unsafe_unretained x; // should be __weak + id __strong y; + id __autoreleasing z; // expected-error {{ivars cannot have __autoreleasing ownership}} +} +@property(weak) id x; // expected-note {{property declared here}} +@property(weak) id y; // expected-note {{property declared here}} +@property(weak) id z; +@end + +@implementation Foo +@synthesize x; // expected-error {{existing ivar 'x' for __weak property 'x' must be __weak}} +@synthesize y; // expected-error {{existing ivar 'y' for __weak property 'y' must be __weak}} +@synthesize z; // suppressed +@end + diff --git a/test/SemaObjC/arc-system-header.m b/test/SemaObjC/arc-system-header.m new file mode 100644 index 000000000000..3f176577156f --- /dev/null +++ b/test/SemaObjC/arc-system-header.m @@ -0,0 +1,50 @@ +// silly workaround expected-note {{marked unavailable here}} +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -isystem %S/Inputs %s -DNO_USE +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -isystem %S/Inputs %s -verify + +// another silly workaround expected-note {{marked unavailable here}} +#include <arc-system-header.h> + +#ifndef NO_USE +void test(id op, void *cp) { + cp = test0(op); // expected-error {{'test0' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}} + cp = *test1(&op); // expected-error {{'test1' is unavailable: converts between Objective-C and C pointers in -fobjc-arc}} +} + +// workaround expected-note {{marked unavailable here}} +void test3(struct Test3 *p) { + p->field = 0; // expected-error {{'field' is unavailable: this system declaration uses an unsupported type}} +} + +// workaround expected-note {{marked unavailable here}} +void test4(Test4 *p) { + p->field1 = 0; // expected-error {{'field1' is unavailable: this system declaration uses an unsupported type}} + p->field2 = 0; +} + +// workaround expected-note {{marked unavailable here}} +void test5(struct Test5 *p) { + p->field = 0; // expected-error {{'field' is unavailable: this system field has retaining ownership}} +} + +id test6() { + // This is actually okay to use if declared in a system header. + id x; + x = (id) kMagicConstant; + x = (id) (x ? kMagicConstant : kMagicConstant); + x = (id) (x ? kMagicConstant : (void*) 0); + + extern void test6_helper(); + x = (id) (test6_helper(), kMagicConstant); +} + +// workaround expected-note 4 {{marked unavailable here}} +void test7(Test7 *p) { + *p.prop = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} + p.prop = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} + *[p prop] = 0; // expected-error {{'prop' is unavailable: this system declaration uses an unsupported type}} + [p setProp: 0]; // expected-error {{'setProp:' is unavailable: this system declaration uses an unsupported type}} +} +#endif + +// test8 in header diff --git a/test/SemaObjC/arc-type-conversion.m b/test/SemaObjC/arc-type-conversion.m new file mode 100644 index 000000000000..103db5695f49 --- /dev/null +++ b/test/SemaObjC/arc-type-conversion.m @@ -0,0 +1,77 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-nonfragile-abi -fobjc-runtime-has-weak -verify -fblocks %s + +void * cvt(id arg) +{ + void* voidp_val; + (void)(int*)arg; // expected-error {{cast of an Objective-C pointer to 'int *' is disallowed with ARC}} + (void)(id)arg; + (void)(__autoreleasing id*)arg; // expected-error {{cast of an Objective-C pointer to '__autoreleasing id *' is disallowed with ARC}} + (void)(id*)arg; // expected-error {{cast of an Objective-C pointer to '__strong id *' is disallowed with ARC}} + + (void)(__autoreleasing id**)voidp_val; + (void)(void*)voidp_val; + (void)(void**)arg; // expected-error {{cast of an Objective-C pointer to 'void **' is disallowed with ARC}} + cvt((void*)arg); // expected-error {{cast of Objective-C pointer type 'id' to C pointer type 'void *' requires a bridged cast}} \ + // expected-error {{implicit conversion of a non-Objective-C pointer type 'void *' to 'id' is disallowed with ARC}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'void *'}} + cvt(0); + (void)(__strong id**)(0); + return arg; // expected-error {{implicit conversion of an Objective-C pointer to 'void *' is disallowed with ARC}} +} + +void to_void(__strong id *sip, __weak id *wip, + __autoreleasing id *aip, + __unsafe_unretained id *uip) { + void *vp1 = sip; + void *vp2 = wip; + void *vp3 = aip; + void *vp4 = uip; + (void)(void*)sip; + (void)(void*)wip; + (void)(void*)aip; + (void)(void*)uip; + (void)(void*)&sip; + (void)(void*)&wip; + (void)(void*)&aip; + (void)(void*)&uip; +} + +void from_void(void *vp) { + __strong id *sip = (__strong id *)vp; + __weak id *wip = (__weak id *)vp; + __autoreleasing id *aip = (__autoreleasing id *)vp; + __unsafe_unretained id *uip = (__unsafe_unretained id *)vp; + + __strong id **sipp = (__strong id **)vp; + __weak id **wipp = (__weak id **)vp; + __autoreleasing id **aipp = (__autoreleasing id **)vp; + __unsafe_unretained id **uipp = (__unsafe_unretained id **)vp; + + sip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__strong id *' is disallowed with ARC}} + wip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__weak id *' is disallowed with ARC}} + aip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__autoreleasing id *' is disallowed with ARC}} + uip = vp; // expected-error{{implicit conversion of a non-Objective-C pointer type 'void *' to '__unsafe_unretained id *' is disallowed with ARC}} +} + +typedef void (^Block)(); +typedef void (^Block_strong)() __strong; +typedef void (^Block_autoreleasing)() __autoreleasing; + +@class NSString; + +void ownership_transfer_in_cast(void *vp, Block *pblk) { + __strong NSString **sip = (NSString**)(__strong id *)vp; + __weak NSString **wip = (NSString**)(__weak id *)vp; + __autoreleasing id *aip = (id*)(__autoreleasing id *)vp; + __unsafe_unretained id *uip = (id*)(__unsafe_unretained id *)vp; + + __strong id **sipp = (id**)(__strong id **)vp; + __weak id **wipp = (id**)(__weak id **)vp; + __autoreleasing id **aipp = (id**)(__autoreleasing id **)vp; + __unsafe_unretained id **uipp = (id**)(__unsafe_unretained id **)vp; + + Block_strong blk_strong1; + Block_strong blk_strong2 = (Block)blk_strong1; + Block_autoreleasing *blk_auto = (Block*)pblk; +} diff --git a/test/SemaObjC/arc-unavailable-for-weakref.m b/test/SemaObjC/arc-unavailable-for-weakref.m new file mode 100644 index 000000000000..104314e6b1d3 --- /dev/null +++ b/test/SemaObjC/arc-unavailable-for-weakref.m @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify %s +// rdar://9693477 + +__attribute__((objc_arc_weak_reference_unavailable)) +@interface NSOptOut1072 // expected-note {{class is declared here}} +@end + +@interface sub : NSOptOut1072 @end // expected-note 2 {{class is declared here}} + +int main() { + __weak sub *w2; // expected-error {{class is incompatible with __weak references}} + + __weak NSOptOut1072 *ns1; // expected-error {{class is incompatible with __weak references}} + + id obj; + + ns1 = (__weak sub *)obj; // expected-error {{assignment of a weak-unavailable object to a __weak object}} \ + // expected-error {{class is incompatible with __weak references}} +} + +// rdar://9732636 +__attribute__((objc_arc_weak_reference_unavailable)) +@interface NOWEAK ++ (id) new; +@end + +NOWEAK * Test1() { + NOWEAK * strong1 = [NOWEAK new]; + __weak id weak1; + weak1 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + + __weak id weak2 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + return (__weak id)strong1; // expected-error {{cast of weak-unavailable object of type 'NOWEAK *' to a __weak object of type '__weak id'}} +} + +@protocol P @end +@protocol P1 @end + +NOWEAK<P, P1> * Test2() { + NOWEAK<P, P1> * strong1 = 0; + __weak id<P> weak1; + weak1 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + + __weak id<P> weak2 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + return (__weak id<P>)strong1; // expected-error {{cast of weak-unavailable object of type 'NOWEAK<P,P1> *' to a __weak object of type '__weak id<P>'}} +} + diff --git a/test/SemaObjC/arc-unbridged-cast.m b/test/SemaObjC/arc-unbridged-cast.m new file mode 100644 index 000000000000..03c84cfce336 --- /dev/null +++ b/test/SemaObjC/arc-unbridged-cast.m @@ -0,0 +1,18 @@ +// // RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s +// rdar://9744349 + +typedef const struct __CFString * CFStringRef; + +@interface I +@property CFStringRef P; +@end + +@implementation I +@synthesize P; +- (id) Meth { + I* p1 = (id)[p1 P]; + id p2 = (__bridge_transfer id)[p1 P]; + id p3 = (__bridge I*)[p1 P]; + return (id) p1.P; +} +@end diff --git a/test/SemaObjC/arc-unsafe-assigns.m b/test/SemaObjC/arc-unsafe-assigns.m new file mode 100644 index 000000000000..be8f90295e8b --- /dev/null +++ b/test/SemaObjC/arc-unsafe-assigns.m @@ -0,0 +1,41 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s +// rdar://9495837 + +@interface Foo { + __unsafe_unretained id unsafe_ivar; +} + +@property (assign,nonatomic) id unsafe_prop; + +- (id)init; ++ (id)new; ++ (id)alloc; + +-(void)Meth; +@end + +@implementation Foo +@synthesize unsafe_prop; +-(id)init { return self; } ++(id)new { return 0; } ++(id)alloc { return 0; } + +-(void)Meth { + self.unsafe_prop = [Foo new]; // expected-warning {{assigning retained object to unsafe property}} + self->unsafe_ivar = [Foo new]; // expected-warning {{assigning retained object to unsafe_unretained}} + self.unsafe_prop = [[Foo alloc] init]; // expected-warning {{assigning retained object to unsafe property}} + self->unsafe_ivar = [[Foo alloc] init]; // expected-warning {{assigning retained object to unsafe_unretained}} + + __unsafe_unretained id unsafe_var; + unsafe_var = [Foo new]; // expected-warning {{assigning retained object to unsafe_unretained}} + unsafe_var = [[Foo alloc] init]; // expected-warning {{assigning retained object to unsafe_unretained}} +} +@end + +void bar(Foo *f) { + f.unsafe_prop = [Foo new]; // expected-warning {{assigning retained object to unsafe property}} + + __unsafe_unretained id unsafe_var; + unsafe_var = [Foo new]; // expected-warning {{assigning retained object to unsafe_unretained}} + unsafe_var = [[Foo alloc] init]; // expected-warning {{assigning retained object to unsafe_unretained}} +} diff --git a/test/SemaObjC/arc-unsafe_unretained.m b/test/SemaObjC/arc-unsafe_unretained.m new file mode 100644 index 000000000000..77bdded1763b --- /dev/null +++ b/test/SemaObjC/arc-unsafe_unretained.m @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fsyntax-only -verify -fblocks -fobjc-nonfragile-abi %s +// RUN: %clang_cc1 -fsyntax-only -verify -fblocks -fobjc-nonfragile-abi -fobjc-arc %s + +struct X { + __unsafe_unretained id object; + int (^ __unsafe_unretained block)(int, int); +}; + +void f(struct X x) { + x.object = 0; + x.block = ^(int x, int y) { return x + y; }; +} diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m new file mode 100644 index 000000000000..3d190e5c53a0 --- /dev/null +++ b/test/SemaObjC/arc.m @@ -0,0 +1,628 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -fblocks -verify %s + +typedef unsigned long NSUInteger; + +void test0(void (*fn)(int), int val) { + fn(val); +} + +@interface A +- (id)retain; +- (id)autorelease; +- (oneway void)release; +- (void)dealloc; +- (NSUInteger)retainCount; +@end + +void test1(A *a) { + SEL s = @selector(retain); // expected-error {{ARC forbids use of 'retain' in a @selector}} + s = @selector(release); // expected-error {{ARC forbids use of 'release' in a @selector}} + s = @selector(autorelease); // expected-error {{ARC forbids use of 'autorelease' in a @selector}} + s = @selector(dealloc); // expected-error {{ARC forbids use of 'dealloc' in a @selector}} + [a dealloc]; // expected-error {{ARC forbids explicit message send of 'dealloc'}} + [a retain]; // expected-error {{ARC forbids explicit message send of 'retain'}} + [a retainCount]; // expected-error {{ARC forbids explicit message send of 'retainCount'}} + [a release]; // expected-error {{ARC forbids explicit message send of 'release'}} + [a autorelease]; // expected-error {{ARC forbids explicit message send of 'autorelease'}} +} + +@interface Test2 : A +- (void) dealloc; +@end +@implementation Test2 +- (void) dealloc { + // This should maybe just be ignored. We're just going to warn about it for now. + [super dealloc]; // expected-error {{ARC forbids explicit message send of 'dealloc'}} +} +@end + +__weak __strong id x; // expected-error {{the type '__strong id' already has retainment attributes}} + +// rdar://8843638 + +@interface I +- (id)retain; +- (id)autorelease; +- (oneway void)release; +- (NSUInteger)retainCount; +@end + +@implementation I +- (id)retain{return 0;} // expected-error {{ARC forbids implementation of 'retain'}} +- (id)autorelease{return 0;} // expected-error {{ARC forbids implementation of 'autorelease'}} +- (oneway void)release{} // expected-error {{ARC forbids implementation of 'release'}} +- (NSUInteger)retainCount{ return 0; } // expected-error {{ARC forbids implementation of 'retainCount'}} +@end + +@implementation I(CAT) +- (id)retain{return 0;} // expected-error {{ARC forbids implementation of 'retain'}} +- (id)autorelease{return 0;} // expected-error {{ARC forbids implementation of 'autorelease'}} +- (oneway void)release{} // expected-error {{ARC forbids implementation of 'release'}} +- (NSUInteger)retainCount{ return 0; } // expected-error {{ARC forbids implementation of 'retainCount'}} +@end + +// rdar://8861761 + +@interface B +-(id)alloc; +- (id)initWithInt: (int) i; +@end + +void rdar8861761() { + B *o1 = [[B alloc] initWithInt:0]; + B *o2 = [B alloc]; + [o2 initWithInt:0]; // expected-warning {{expression result unused}} +} + +// rdar://8925835 +@interface rdar8925835 +- (void)foo:(void (^)(unsigned captureCount, I * const capturedStrings[captureCount]))block; +@end + +void test5() { + extern void test5_helper(__autoreleasing id *); + id x; + + // Okay because of magic temporaries. + test5_helper(&x); + + __autoreleasing id *a = &x; // expected-error {{initializing '__autoreleasing id *' with an expression of type '__strong id *' changes retain/release properties of pointer}} + + a = &x; // expected-error {{assigning '__strong id *' to '__autoreleasing id *' changes retain/release properties of pointer}} + + extern void test5_helper2(id const *); + test5_helper2(&x); + + extern void test5_helper3(__weak id *); // expected-note {{passing argument to parameter here}} + test5_helper3(&x); // expected-error {{passing '__strong id *' to parameter of type '__weak id *' changes retain/release properties of pointer}} +} + +// rdar://problem/8937869 +void test6(unsigned cond) { + switch (cond) { + case 0: + ; + id x; // expected-note {{jump bypasses initialization of retaining variable}} + + case 1: // expected-error {{switch case is in protected scope}} + break; + } +} + +@class NSError; +void test7(void) { + extern void test7_helper(NSError **); + NSError *err; + test7_helper(&err); +} +void test7_weak(void) { + extern void test7_helper(NSError **); + __weak NSError *err; + test7_helper(&err); +} +void test7_unsafe(void) { + extern void test7_helper(NSError **); // expected-note {{passing argument to parameter here}} + __unsafe_unretained NSError *err; + test7_helper(&err); // expected-error {{passing 'NSError *__unsafe_unretained *' to parameter of type 'NSError *__autoreleasing *' changes retain/release properties of pointer}} +} + +@class Test8_incomplete; +@interface Test8_complete @end; +@interface Test8_super @end; +@interface Test8 : Test8_super +- (id) init00; +- (id) init01; // expected-note {{declaration in interface}} \ + // expected-note{{overridden method}} +- (id) init02; // expected-note{{overridden method}} +- (id) init03; // covariance +- (id) init04; // covariance +- (id) init05; // expected-note{{overridden method}} + +- (void) init10; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init11; +- (void) init12; +- (void) init13; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init14; // expected-note {{declaration in interface is not in the 'init' family because its result type is not an object pointer}} +- (void) init15; + +// These should be invalid to actually call. +- (Test8_incomplete*) init20; +- (Test8_incomplete*) init21; // expected-note {{declaration in interface}} +- (Test8_incomplete*) init22; +- (Test8_incomplete*) init23; +- (Test8_incomplete*) init24; +- (Test8_incomplete*) init25; + +- (Test8_super*) init30; // id exception to covariance +- (Test8_super*) init31; // expected-note {{declaration in interface}} \ + // expected-note{{overridden method}} +- (Test8_super*) init32; // expected-note{{overridden method}} +- (Test8_super*) init33; +- (Test8_super*) init34; // covariance +- (Test8_super*) init35; // expected-note{{overridden method}} + +- (Test8*) init40; // id exception to covariance +- (Test8*) init41; // expected-note {{declaration in interface}} \ + // expected-note{{overridden method}} +- (Test8*) init42; // expected-note{{overridden method}} +- (Test8*) init43; // this should be a warning, but that's a general language thing, not an ARC thing +- (Test8*) init44; +- (Test8*) init45; // expected-note{{overridden method}} + +- (Test8_complete*) init50; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init51; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init52; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init53; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init54; // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init55; // expected-error {{init methods must return a type related to the receiver type}} +@end +@implementation Test8 +- (id) init00 { return 0; } +- (id) init10 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (id) init20 { return 0; } +- (id) init30 { return 0; } +- (id) init40 { return 0; } +- (id) init50 { return 0; } + +- (void) init01 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} \ + // expected-warning{{ method is expected to return an instance of its class type 'Test8', but is declared to return 'void'}} +- (void) init11 {} +- (void) init21 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} +- (void) init31 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} \ + // expected-warning{{ method is expected to return an instance of its class type 'Test8', but is declared to return 'void'}} +- (void) init41 {} // expected-error {{method was declared as an 'init' method, but its implementation doesn't match because its result type is not an object pointer}} \ + // expected-warning{{ method is expected to return an instance of its class type 'Test8', but is declared to return 'void'}} +- (void) init51 {} + +- (Test8_incomplete*) init02 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_incomplete *'}} +- (Test8_incomplete*) init12 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init22 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_incomplete*) init32 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_incomplete *'}} +- (Test8_incomplete*) init42 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_incomplete *'}} +- (Test8_incomplete*) init52 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} + +- (Test8_super*) init03 { return 0; } +- (Test8_super*) init13 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (Test8_super*) init23 { return 0; } +- (Test8_super*) init33 { return 0; } +- (Test8_super*) init43 { return 0; } +- (Test8_super*) init53 { return 0; } + +- (Test8*) init04 { return 0; } +- (Test8*) init14 { return 0; } // expected-error {{method implementation does not match its declaration}} +- (Test8*) init24 { return 0; } +- (Test8*) init34 { return 0; } +- (Test8*) init44 { return 0; } +- (Test8*) init54 { return 0; } + +- (Test8_complete*) init05 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_complete *'}} +- (Test8_complete*) init15 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init25 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +- (Test8_complete*) init35 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_complete *'}} +- (Test8_complete*) init45 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} \ + // expected-warning{{method is expected to return an instance of its class type 'Test8', but is declared to return 'Test8_complete *'}} +- (Test8_complete*) init55 { return 0; } // expected-error {{init methods must return a type related to the receiver type}} +@end + +@class Test9_incomplete; +@interface Test9 +- (Test9_incomplete*) init1; // expected-error {{init methods must return a type related to the receiver type}} +- (Test9_incomplete*) init2; +@end +id test9(Test9 *v) { + return [v init1]; +} + +// Test that the inference rules are different for fast enumeration variables. +void test10(id collection) { + for (id x in collection) { + __strong id *ptr = &x; // expected-warning {{initializing '__strong id *' with an expression of type 'const __strong id *' discards qualifiers}} + } + + for (__strong id x in collection) { + __weak id *ptr = &x; // expected-error {{initializing '__weak id *' with an expression of type '__strong id *' changes retain/release properties of pointer}} + } +} + +// rdar://problem/9078626 +#define nil ((void*) 0) +void test11(id op, void *vp) { + _Bool b; + b = (op == nil); + b = (nil == op); + + b = (vp == nil); + b = (nil == vp); + + b = (vp == op); // expected-error {{implicit conversion of an Objective-C pointer to 'void *'}} + b = (op == vp); // expected-error {{implicit conversion of a non-Objective-C pointer type 'void *' to 'id'}} +} + +void test12(id collection) { + for (id x in collection) { + x = 0; // expected-error {{fast enumeration variables can't be modified in ARC by default; declare the variable __strong to allow this}} + } + + for (const id x in collection) { + x = 0; // expected-error {{read-only variable is not assignable}} + } + + for (__strong id x in collection) { + x = 0; + } +} + +@interface Test13 +- (id) init0; +- (void) noninit; +@end +@implementation Test13 +- (id) init0 { + self = 0; +} +- (void) noninit { + self = 0; // expected-error {{cannot assign to 'self' outside of a method in the init family}} +} +@end + +// rdar://problem/9172151 +@class Test14A, Test14B; +void test14() { + extern void test14_consume(id *); + extern int test14_cond(void); + extern float test14_nowriteback(id __autoreleasing const *); // expected-note{{passing argument to parameter here}} + + Test14A *a; + Test14B *b; + id i; + id cla[10]; + id vla[test14_cond() + 10]; + + test14_consume((__strong id*) &a); + test14_consume((test14_cond() ? (__strong id*) &b : &i)); + test14_consume(test14_cond() ? 0 : &a); + test14_consume(test14_cond() ? (void*) 0 : (&a)); + test14_consume(cla); // expected-error {{passing address of non-scalar object to __autoreleasing parameter for write-back}} + test14_consume(vla); // expected-error {{passing address of non-scalar object to __autoreleasing parameter for write-back}} + test14_consume(&cla[5]); // expected-error {{passing address of non-scalar object to __autoreleasing parameter for write-back}} + + __strong id *test14_indirect(void); + test14_consume(test14_indirect()); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + extern id test14_global; + test14_consume(&test14_global); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + extern __strong id *test14_global_ptr; + test14_consume(test14_global_ptr); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + static id static_local; + test14_consume(&static_local); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + __weak id* wip; + test14_nowriteback(&static_local); // okay, not a write-back. + test14_nowriteback(wip); // expected-error{{passing '__weak id *' to parameter of type '__autoreleasing id const *' changes retain/release properties of pointer}} +} + +void test15() { + __block __autoreleasing id x; // expected-error {{__block variables cannot have __autoreleasing ownership}} +} + +struct Test16; +@interface Test16a +- (void) test16_0: (int) x; +- (int) test16_1: (int) x; // expected-note {{one possibility}} +- (int) test16_2: (int) x; // expected-note {{one possibility}} +- (id) test16_3: (int) x __attribute__((ns_returns_retained)); // expected-note {{one possibility}} +- (void) test16_4: (int) x __attribute__((ns_consumes_self)); // expected-note {{one possibility}} +- (void) test16_5: (id) __attribute__((ns_consumed)) x; // expected-note {{one possibility}} +- (void) test16_6: (id) x; +@end + +@interface Test16b +- (void) test16_0: (int) x; +- (int) test16_1: (char*) x; // expected-note {{also found}} +- (char*) test16_2: (int) x; // expected-note {{also found}} +- (id) test16_3: (int) x; // expected-note {{also found}} +- (void) test16_4: (int) x; // expected-note {{also found}} +- (void) test16_5: (id) x; // expected-note {{also found}} +- (void) test16_6: (struct Test16 *) x; +@end + +void test16(void) { + id v; + [v test16_0: 0]; + [v test16_1: 0]; // expected-error {{multiple methods named 'test16_1:' found with mismatched result, parameter type or attributes}} + [v test16_2: 0]; // expected-error {{multiple methods named}} + [v test16_3: 0]; // expected-error {{multiple methods named}} + [v test16_4: 0]; // expected-error {{multiple methods named}} + [v test16_5: 0]; // expected-error {{multiple methods named}} + [v test16_6: 0]; +} + +@class Test17; +@protocol Test17p +- (void) test17; ++ (void) test17; +@end +void test17(void) { + Test17 *v0; + [v0 test17]; // expected-error {{receiver type 'Test17' for instance message is a forward declaration}} + + Test17<Test17p> *v1; + [v1 test17]; // expected-error {{receiver type 'Test17<Test17p>' for instance message is a forward declaration}} + + [Test17 test17]; // expected-error {{receiver 'Test17' for class message is a forward declaration}} +} + +void test18(void) { + id x; + [x test18]; // expected-error {{no known instance method for selector 'test18'}} +} + +extern struct Test19 *test19a; +struct Test19 *const test19b = 0; +void test19(void) { + id x; + x = (id) test19a; // expected-error {{bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership))}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'struct Test19 *' into ARC}} + x = (id) test19b; // expected-error {{bridged cast}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_transfer to transfer ownership of a +1 'struct Test19 *' into ARC}} +} + +// rdar://problem/8951453 +static __thread id test20_implicit; // expected-error {{thread-local variable has non-trivial ownership: type is '__strong id'}} +static __thread __strong id test20_strong; // expected-error {{thread-local variable has non-trivial ownership: type is '__strong id'}} +static __thread __weak id test20_weak; // expected-error {{thread-local variable has non-trivial ownership: type is '__weak id'}} +static __thread __autoreleasing id test20_autoreleasing; // expected-error {{thread-local variable has non-trivial ownership: type is '__autoreleasing id'}} expected-error {{global variables cannot have __autoreleasing ownership}} +static __thread __unsafe_unretained id test20_unsafe; +void test20(void) { + static __thread id test20_implicit; // expected-error {{thread-local variable has non-trivial ownership: type is '__strong id'}} + static __thread __strong id test20_strong; // expected-error {{thread-local variable has non-trivial ownership: type is '__strong id'}} + static __thread __weak id test20_weak; // expected-error {{thread-local variable has non-trivial ownership: type is '__weak id'}} + static __thread __autoreleasing id test20_autoreleasing; // expected-error {{thread-local variable has non-trivial ownership: type is '__autoreleasing id'}} expected-error {{global variables cannot have __autoreleasing ownership}} + static __thread __unsafe_unretained id test20_unsafe; +} + +// rdar://9310049 +_Bool fn(id obj) { + return (_Bool)obj; +} + +// Check casting w/ ownership qualifiers. +void test21() { + __strong id *sip; + (void)(__weak id *)sip; // expected-error{{casting '__strong id *' to type '__weak id *' changes retain/release properties of pointer}} + (void)(__weak const id *)sip; // expected-error{{casting '__strong id *' to type '__weak id const *' changes retain/release properties of pointer}} + (void)(__autoreleasing id *)sip; // expected-error{{casting '__strong id *' to type '__autoreleasing id *' changes retain/release properties of pointer}} + (void)(__autoreleasing const id *)sip; // okay +} + +// rdar://problem/9340462 +void test22(id x[]) { // expected-error {{must explicitly describe intended ownership of an object array parameter}} +} + +// rdar://problem/9400219 +void test23(void) { + void *ptr; + ptr = @"foo"; + ptr = (ptr ? @"foo" : 0); + ptr = (ptr ? @"foo" : @"bar"); +} + +id test24(void) { + extern void test24_helper(void); + return test24_helper(), (void*) 0; +} + +// rdar://9400841 +@interface Base +@property (assign) id content; +@end + +@interface Foo : Base +-(void)test; +@end + +@implementation Foo +-(void)test { + super.content = 0; +} +@end + +// <rdar://problem/9398437> +void test25(Class *classes) { + Class *other_classes; + test25(other_classes); +} + +void test26(id y) { + extern id test26_var1; + __sync_swap(&test26_var1, 0, y); // expected-error {{cannot perform atomic operation on a pointer to type '__strong id': type has non-trivial ownership}} + + extern __unsafe_unretained id test26_var2; + __sync_swap(&test26_var2, 0, y); +} + +@interface Test26 +- (id) init; +- (id) initWithInt: (int) x; +@end +@implementation Test26 +- (id) init { return self; } +- (id) initWithInt: (int) x { + [self init]; // expected-error {{the result of a delegate init call must be immediately returned or assigned to 'self'}} + return self; +} +@end + +// rdar://9525555 +@interface Test27 +@property id x; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}} \ + // expected-warning {{default property attribute 'assign' not appropriate for non-gc object}} \ + // expected-note {{declared here}} +@property (readonly) id ro; // expected-note {{declared here}} +@property (readonly) id custom_ro; +@property int y; +@end + +@implementation Test27 +@synthesize x; // expected-error {{ARC forbids synthesizing a property of an Objective-C object with unspecified storage attribute}} +@synthesize ro; // expected-error {{ARC forbids synthesizing a property of an Objective-C object with unspecified storage attribute}} +@synthesize y; +-(id)custom_ro { return 0; } +@end + +// rdar://9569264 +@interface Test28 +@property (nonatomic, assign) __strong id a; // expected-error {{unsafe_unretained property 'a' may not also be declared __strong}} +@end + +@interface Test28 () +@property (nonatomic, assign) __strong id b; // expected-error {{unsafe_unretained property 'b' may not also be declared __strong}} +@end + +@implementation Test28 +@synthesize a; +@synthesize b; +@end + +// rdar://9573962 +typedef struct Bark Bark; +@interface Test29 +@property Bark* P; +@end + +@implementation Test29 +@synthesize P; +- (id)Meth { + Bark** f = &P; + return 0; +} +@end + +// rdar://9495837 +@interface Test30 ++ (id) new; +- (void)Meth; +@end + +@implementation Test30 ++ (id) new { return 0; } +- (void) Meth { + __weak id x = [Test30 new]; // expected-warning {{assigning retained object to weak variable}} + id __unsafe_unretained u = [Test30 new]; // expected-warning {{assigning retained object to unsafe_unretained variable}} + id y = [Test30 new]; + x = [Test30 new]; // expected-warning {{assigning retained object to weak variable}} + u = [Test30 new]; // expected-warning {{assigning retained object to unsafe_unretained variable}} + y = [Test30 new]; +} +@end + +// rdar://9411838 +@protocol PTest31 @end + +int Test31() { + Class cls; + id ids; + id<PTest31> pids; + Class<PTest31> pcls; + + int i = (ids->isa ? 1 : 0); // expected-error {{member reference base type 'id' is not a structure or union}} + int j = (pids->isa ? 1 : 0); // expected-error {{member reference base type 'id<PTest31>' is not a structure or union}} + int k = (pcls->isa ? i : j); // expected-error {{member reference base type 'Class<PTest31>' is not a structure or union}} + return cls->isa ? i : j; // expected-error {{member reference base type 'Class' is not a structure or union}} +} + +// rdar://9612030 +@interface ITest32 { +@public + id ivar; +} +@end + +id Test32(__weak ITest32 *x) { + __weak ITest32 *y; + x->ivar = 0; // expected-error {{dereferencing a __weak pointer is not allowed}} + return y ? y->ivar // expected-error {{dereferencing a __weak pointer is not allowed}} + : (*x).ivar; // expected-error {{dereferencing a __weak pointer is not allowed}} +} + +// rdar://9619861 +extern int printf(const char*, ...); +typedef long intptr_t; + +int Test33(id someid) { + printf( "Hello%ld", (intptr_t)someid); + return (int)someid; +} + +// rdar://9636091 +@interface I34 +@property (nonatomic, retain) id newName __attribute__((ns_returns_not_retained)) ; + +@property (nonatomic, retain) id newName1 __attribute__((ns_returns_not_retained)) ; +- (id) newName1 __attribute__((ns_returns_not_retained)); + +@property (nonatomic, retain) id newName2 __attribute__((ns_returns_not_retained)); // expected-note {{roperty declared here}} +- (id) newName2; // expected-warning {{property declared as returning non-retained objects; getter returning retained objects}} +@end + +@implementation I34 +@synthesize newName; + +@synthesize newName1; +- (id) newName1 { return 0; } + +@synthesize newName2; +@end + +void test35(void) { + extern void test36_helper(id*); + id x; + __strong id *xp = 0; + + test36_helper(&x); + test36_helper(xp); // expected-error {{passing address of non-local object to __autoreleasing parameter for write-back}} + + // rdar://problem/9665710 + __block id y; + test36_helper(&y); + ^{ test36_helper(&y); }(); + + __strong int non_objc_type; // expected-warning {{'__strong' only applies to objective-c object or block pointer types}} +} + +void test36(int first, ...) { + // <rdar://problem/9758798> + __builtin_va_list arglist; + __builtin_va_start(arglist, first); + id obj = __builtin_va_arg(arglist, id); + __builtin_va_end(arglist); +} diff --git a/test/SemaObjC/autoreleasepool.m b/test/SemaObjC/autoreleasepool.m new file mode 100644 index 000000000000..41e17681afdf --- /dev/null +++ b/test/SemaObjC/autoreleasepool.m @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +void *objc_autoreleasepool_push(); +void autoreleasepool_pop(void*); + +@interface AUTORP @end + +@implementation AUTORP +- (void) unregisterTask:(id) task { + goto L; // expected-error {{goto into protected scope}} + + @autoreleasepool { // expected-note {{jump bypasses auto release push of @autoreleasepool block}} + void *tmp = objc_autoreleasepool_push(); + L: + autoreleasepool_pop(tmp); + @autoreleasepool { + return; + } + } +} +@end + diff --git a/test/SemaObjC/class-proto-1.m b/test/SemaObjC/class-proto-1.m index 246b5002f67f..80309764335b 100644 --- a/test/SemaObjC/class-proto-1.m +++ b/test/SemaObjC/class-proto-1.m @@ -23,9 +23,9 @@ @interface E2 <p1,p2,p3> @end // expected-warning {{cannot find protocol definition for 'p3'}} -@class U1, U2; +@class U1, U2; // expected-note {{forward class is declared here}} -@interface E3 : U1 @end // expected-error {{cannot find interface declaration for 'U1', superclass of 'E3'}} +@interface E3 : U1 @end // expected-error {{attempting to use the forward class 'U1' as superclass of 'E3'}} @interface I3 : E3 @end diff --git a/test/SemaObjC/class-unavail-warning.m b/test/SemaObjC/class-unavail-warning.m index 426ac772c8aa..408647ac11c1 100644 --- a/test/SemaObjC/class-unavail-warning.m +++ b/test/SemaObjC/class-unavail-warning.m @@ -2,7 +2,7 @@ // rdar://9092208 __attribute__((unavailable("not available"))) -@interface MyClass { // expected-note 5 {{function has been explicitly marked unavailable here}} +@interface MyClass { // expected-note 5 {{declaration has been explicitly marked unavailable here}} @public void *_test; } diff --git a/test/SemaObjC/debugger-support.m b/test/SemaObjC/debugger-support.m new file mode 100644 index 000000000000..21c096edad43 --- /dev/null +++ b/test/SemaObjC/debugger-support.m @@ -0,0 +1,14 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -fdebugger-support %s -emit-llvm -o - | FileCheck %s + +// rdar://problem/9416370 +void test0(id x) { + struct A { int w, x, y, z; }; + struct A result = (struct A) [x makeStruct]; + // CHECK: define void @test0( + // CHECK: [[X:%.*]] = alloca i8*, align 8 + // CHECK-NEXT: [[RESULT:%.*]] = alloca [[A:%.*]], align 4 + // CHECK-NEXT: store i8* {{%.*}}, i8** [[X]], + // CHECK-NEXT: [[T0:%.*]] = load i8** [[X]], + // CHECK-NEXT: [[T1:%.*]] = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" + // CHECK-NEXT: [[T2:%.*]] = call { i64, i64 } bitcast (i8* (i8*, i8*, ...)* @objc_msgSend to { i64, i64 } (i8*, i8*)*)(i8* [[T0]], i8* [[T1]]) +} diff --git a/test/SemaObjC/error-property-gc-attr.m b/test/SemaObjC/error-property-gc-attr.m index a77b68bff0ea..829c08228747 100644 --- a/test/SemaObjC/error-property-gc-attr.m +++ b/test/SemaObjC/error-property-gc-attr.m @@ -20,9 +20,9 @@ @implementation INTF @synthesize pweak=IVAR; // expected-error {{existing ivar 'IVAR' for __weak property 'pweak' must be __weak}} -@synthesize NOT=II; // expected-error {{property 'NOT' must be declared __weak to match existing ivar 'II' with __weak attribute}} +@synthesize NOT=II; // expected-error {{existing ivar 'II' for strong property 'NOT' may not be __weak}} @synthesize WID; @synthesize ID; -@synthesize AWEAK; // expected-error {{property 'AWEAK' must be declared __weak to match existing ivar 'AWEAK' with __weak attribute}} +@synthesize AWEAK; // expected-error {{existing ivar 'AWEAK' for strong property 'AWEAK' may not be __weak}} @synthesize WI; @end diff --git a/test/SemaObjC/forward-class-1.m b/test/SemaObjC/forward-class-1.m index ab213fb4ce72..de94e884aee2 100644 --- a/test/SemaObjC/forward-class-1.m +++ b/test/SemaObjC/forward-class-1.m @@ -1,9 +1,9 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@class FOO, BAR; +@class FOO, BAR; // expected-note {{forward class is declared here}} @class FOO, BAR; -@interface INTF : FOO // expected-error {{cannot find interface declaration for 'FOO', superclass of 'INTF'}} +@interface INTF : FOO // expected-error {{attempting to use the forward class 'FOO' as superclass of 'INTF'}} @end @interface FOO @@ -45,3 +45,14 @@ typedef NSObject <XCElementP> XCElement; @end +// rdar://9653341 +@class B; // expected-note {{forward class is declared here}} +@interface A : B {} // expected-error {{attempting to use the forward class 'B' as superclass of 'A'}} +@end + +@interface B : A {} +@end + +@implementation A @end +@implementation B @end + diff --git a/test/SemaObjC/ivar-ref-misuse.m b/test/SemaObjC/ivar-ref-misuse.m index ba2f11594f96..f6d9c9408445 100644 --- a/test/SemaObjC/ivar-ref-misuse.m +++ b/test/SemaObjC/ivar-ref-misuse.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@interface Sprite { +@interface Sprite { // expected-note{{'Sprite' declared here}} int sprite, spree; int UseGlobalBar; } @@ -17,7 +17,8 @@ int UseGlobalBar; + (void)setFoo:(int)foo { sprite = foo; // expected-error {{instance variable 'sprite' accessed in class method}} spree = foo; - Xsprite = foo; // expected-error {{use of undeclared identifier 'Xsprite'}} + Xsprite = foo; // expected-error {{unknown type name 'Xsprite'; did you mean 'Sprite'?}} \ + // expected-error{{expected identifier or '('}} UseGlobalBar = 10; } + (void)setSprite:(int)sprite { diff --git a/test/SemaObjC/method-lookup-3.m b/test/SemaObjC/method-lookup-3.m index 882b3d1d2123..b3d9c46484c3 100644 --- a/test/SemaObjC/method-lookup-3.m +++ b/test/SemaObjC/method-lookup-3.m @@ -55,3 +55,19 @@ void f5(id a0, Abstract *a1) { void f6(id<A> a0) { Abstract *l = [a0 x]; } + +struct test3a { int x, y; }; +struct test3b { unsigned x, y; }; +@interface Test3A - (struct test3a) test3; @end +@interface Test3B - (struct test3b) test3; @end +void test3(id x) { + (void) [x test3]; +} + +struct test4a { int x, y; }; +struct test4b { float x, y; }; +@interface Test4A - (struct test4a) test4; @end //expected-note{{using}} +@interface Test4B - (struct test4b) test4; @end //expected-note{{also found}} +void test4(id x) { + (void) [x test4]; //expected-warning {{multiple methods named 'test4' found}} +} diff --git a/test/SemaObjC/no-warning-unavail-unimp.m b/test/SemaObjC/no-warning-unavail-unimp.m new file mode 100644 index 000000000000..94093222e8c9 --- /dev/null +++ b/test/SemaObjC/no-warning-unavail-unimp.m @@ -0,0 +1,12 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// rdar://9651605 + +@interface Foo +@property (getter=getVal) int val __attribute__((unavailable)); +- Method __attribute__((unavailable)); ++ CMethod __attribute__((unavailable)); +@end + +@implementation Foo +@end + diff --git a/test/SemaObjC/property-10.m b/test/SemaObjC/property-10.m index 011927368fa3..e89d68e9d051 100644 --- a/test/SemaObjC/property-10.m +++ b/test/SemaObjC/property-10.m @@ -5,14 +5,24 @@ @interface I0 @property(readonly, readwrite) int p0; // expected-error {{property attributes 'readonly' and 'readwrite' are mutually exclusive}} -@property(retain) int p1; // expected-error {{property with 'retain' attribute must be of object type}} +@property(retain) int p1; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} +@property(strong) int s1; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} @property(copy) int p2; // expected-error {{property with 'copy' attribute must be of object type}} @property(assign, copy) id p3_0; // expected-error {{property attributes 'assign' and 'copy' are mutually exclusive}} @property(assign, retain) id p3_1; // expected-error {{property attributes 'assign' and 'retain' are mutually exclusive}} +@property(assign, strong) id s3_1; // expected-error {{property attributes 'assign' and 'strong' are mutually exclusive}} @property(copy, retain) id p3_2; // expected-error {{property attributes 'copy' and 'retain' are mutually exclusive}} +@property(copy, strong) id s3_2; // expected-error {{property attributes 'copy' and 'strong' are mutually exclusive}} @property(assign, copy, retain) id p3_3; // expected-error {{property attributes 'assign' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'assign' and 'retain' are mutually exclusive}} +@property(assign, copy, strong) id s3_3; // expected-error {{property attributes 'assign' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'assign' and 'strong' are mutually exclusive}} + +@property(unsafe_unretained, copy) id p4_0; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}} +@property(unsafe_unretained, retain) id p4_1; // expected-error {{property attributes 'unsafe_unretained' and 'retain' are mutually exclusive}} +@property(unsafe_unretained, strong) id s4_1; // expected-error {{property attributes 'unsafe_unretained' and 'strong' are mutually exclusive}} +@property(unsafe_unretained, copy, retain) id p4_3; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'unsafe_unretained' and 'retain' are mutually exclusive}} +@property(unsafe_unretained, copy, strong) id s4_3; // expected-error {{property attributes 'unsafe_unretained' and 'copy' are mutually exclusive}}, expected-error {{property attributes 'unsafe_unretained' and 'strong' are mutually exclusive}} @property id p4; // expected-warning {{no 'assign', 'retain', or 'copy' attribute is specified - 'assign' is assumed}}, expected-warning {{default property attribute 'assign' not appropriate for non-gc object}} @@ -22,7 +32,8 @@ @end @interface I0() -@property (retain) int PROP; // expected-error {{property with 'retain' attribute must be of object type}} +@property (retain) int PROP; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} +@property (strong) int SPROP; // expected-error {{property with 'retain (or strong)' attribute must be of object type}} @property(nonatomic,copy) int (*PROP1)(); // expected-error {{property with 'copy' attribute must be of object type}} +@property(nonatomic,weak) int (*PROP2)(); // expected-error {{property with 'weak' attribute must be of object type}} @end - diff --git a/test/SemaObjC/property-inherited.m b/test/SemaObjC/property-inherited.m index 5c5631e6fb89..11ef2befa99b 100644 --- a/test/SemaObjC/property-inherited.m +++ b/test/SemaObjC/property-inherited.m @@ -1,6 +1,8 @@ // RUN: %clang_cc1 %s -fsyntax-only -verify +// RUN: %clang_cc1 -x objective-c++ %s -fsyntax-only -verify -// <rdar://problem/6497242> Inherited overridden protocol declared objects don't work +// rdar://6497242 Inherited overridden protocol declared objects don't work +// rdar://9740328 Case for c++ @protocol NSObject @end @interface NSObject @end diff --git a/test/SemaObjC/property-ns-returns-not-retained-attr.m b/test/SemaObjC/property-ns-returns-not-retained-attr.m new file mode 100644 index 000000000000..187c93f3d3af --- /dev/null +++ b/test/SemaObjC/property-ns-returns-not-retained-attr.m @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -verify %s +// rdar://9636091 + +@interface I +@property (nonatomic, retain) id newName __attribute__((ns_returns_not_retained)) ; + +@property (nonatomic, retain) id newName1 __attribute__((ns_returns_not_retained)) ; +- (id) newName1 __attribute__((ns_returns_not_retained)); + +@property (nonatomic, retain) id newName2 __attribute__((ns_returns_not_retained)); // expected-note {{roperty declared here}} +- (id) newName2; // expected-warning {{property declared as returning non-retained objects; getter returning retained objects}} +@end + +@implementation I +@synthesize newName; + +@synthesize newName1; +- (id) newName1 { return 0; } + +@synthesize newName2; +@end diff --git a/test/SemaObjC/related-result-type-inference.m b/test/SemaObjC/related-result-type-inference.m index f5399180c400..094f19a67129 100644 --- a/test/SemaObjC/related-result-type-inference.m +++ b/test/SemaObjC/related-result-type-inference.m @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fobjc-infer-related-result-type -verify %s +// RUN: %clang_cc1 -verify %s @interface Unrelated @end diff --git a/test/SemaObjC/selector-3.m b/test/SemaObjC/selector-3.m index 69a74f830ce5..b248a5d036ab 100644 --- a/test/SemaObjC/selector-3.m +++ b/test/SemaObjC/selector-3.m @@ -27,3 +27,28 @@ SEL func() { return @selector(length); // expected-warning {{unimplemented selector 'length'}} } + +// rdar://9545564 +@class MSPauseManager; + +@protocol MSPauseManagerDelegate +@optional +- (void)pauseManagerDidPause:(MSPauseManager *)manager; +- (int)respondsToSelector:(SEL)aSelector; +@end + +@interface MSPauseManager +{ + id<MSPauseManagerDelegate> _delegate; +} +@end + + +@implementation MSPauseManager +- (id) Meth { + if ([_delegate respondsToSelector:@selector(pauseManagerDidPause:)]) + return 0; + return 0; +} +@end + diff --git a/test/SemaObjC/self-declared-in-block.m b/test/SemaObjC/self-declared-in-block.m index 4bd720214438..21310953c2c5 100644 --- a/test/SemaObjC/self-declared-in-block.m +++ b/test/SemaObjC/self-declared-in-block.m @@ -7,11 +7,12 @@ @implementation Blocky { int _a; } -- (void)doAThing { +- (int)doAThing { ^{ - char self; // expected-note {{declared here}} - _a; // expected-error {{instance variable '_a' cannot be accessed because 'self' has been redeclared}} + char self; + return _a; }(); + return _a; } @end @@ -37,14 +38,14 @@ (void)_anIvar; } { - C* self; // expected-note {{declared here}} - (void) _anIvar; // expected-error {{instance variable '_anIvar' cannot be accessed because 'self' has been redeclared}} + C* self; + (void) _anIvar; } } - (void)doAThing { ^{ - id self; // expected-note {{declared here}} - (void)_anIvar; // expected-error {{instance variable '_anIvar' cannot be accessed because 'self' has been redeclared}} + id self; + (void)_anIvar; }(); } @end diff --git a/test/SemaObjC/sizeof-interface.m b/test/SemaObjC/sizeof-interface.m index 3fe39645fddb..65c8e49e0b87 100644 --- a/test/SemaObjC/sizeof-interface.m +++ b/test/SemaObjC/sizeof-interface.m @@ -7,7 +7,7 @@ int g0 = sizeof(I0); // expected-error{{invalid application of 'sizeof' to an in // rdar://6821047 void *g3(I0 *P) { - P = P+5; // expected-error {{arithmetic on pointer to incomplete type 'I0 *'}} + P = P+5; // expected-error {{arithmetic on a pointer to an incomplete type 'I0'}} return &P[4]; // expected-error{{subscript of pointer to incomplete type 'I0'}} } diff --git a/test/SemaObjC/special-dep-unavail-warning.m b/test/SemaObjC/special-dep-unavail-warning.m index 1d07a49e619d..57a2fa346569 100644 --- a/test/SemaObjC/special-dep-unavail-warning.m +++ b/test/SemaObjC/special-dep-unavail-warning.m @@ -3,24 +3,24 @@ @interface B - (void) depInA; -- (void) unavailMeth __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) unavailMeth __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depInA1 __attribute__((deprecated)); - (void) unavailMeth1; - (void) depInA2 __attribute__((deprecated)); -- (void) unavailMeth2 __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) unavailMeth2 __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depunavailInA; -- (void) depunavailInA1 __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) depunavailInA1 __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void)FuzzyMeth __attribute__((deprecated)); - (void)FuzzyMeth1 __attribute__((unavailable)); @end @interface A -- (void) unavailMeth1 __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) unavailMeth1 __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depInA __attribute__((deprecated)); - (void) depInA2 __attribute__((deprecated)); - (void) depInA1; - (void) unavailMeth2 __attribute__((unavailable)); -- (void) depunavailInA __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{function has been explicitly marked unavailable here}} +- (void) depunavailInA __attribute__((deprecated)) __attribute__((unavailable)); // expected-note {{has been explicitly marked unavailable here}} - (void) depunavailInA1; - (void)FuzzyMeth __attribute__((unavailable)); - (void)FuzzyMeth1 __attribute__((deprecated)); diff --git a/test/SemaObjC/synthesized-ivar.m b/test/SemaObjC/synthesized-ivar.m index 3bc372bcd81e..4786d808fa2f 100644 --- a/test/SemaObjC/synthesized-ivar.m +++ b/test/SemaObjC/synthesized-ivar.m @@ -51,3 +51,11 @@ int f0(I *a) { return a->IP; } // expected-error {{instance variable 'IP' is pri } @end +@interface A +@property (weak) id testObjectWeakProperty; // expected-note {{declared here}} +@end + +@implementation A +// rdar://9605088 +@synthesize testObjectWeakProperty; // expected-error {{@synthesize of 'weak' property is only allowed in ARC or GC mode}} +@end diff --git a/test/SemaObjC/typedef-class.m b/test/SemaObjC/typedef-class.m index 0f7e6822c4f9..c983195b243e 100644 --- a/test/SemaObjC/typedef-class.m +++ b/test/SemaObjC/typedef-class.m @@ -5,7 +5,7 @@ typedef struct _NSZone NSZone; @class NSInvocation, NSMethodSignature, NSCoder, NSString, NSEnumerator; -@protocol NSObject - (BOOL) isEqual:(id) object; @end +@protocol NSObject - (BOOL) isEqual:(id) object; - (id)init; @end @protocol NSCopying - (id) copyWithZone:(NSZone *) zone; @end @protocol NSCoding - (void) encodeWithCoder:(NSCoder *) aCoder; @end diff --git a/test/SemaObjC/undef-superclass-1.m b/test/SemaObjC/undef-superclass-1.m index c941f82e435c..41cf1439bd9c 100644 --- a/test/SemaObjC/undef-superclass-1.m +++ b/test/SemaObjC/undef-superclass-1.m @@ -1,8 +1,8 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@class SUPER, Y; +@class SUPER, Y; // expected-note 2 {{forward class is declared here}} -@interface INTF :SUPER // expected-error {{cannot find interface declaration for 'SUPER', superclass of 'INTF'}} +@interface INTF :SUPER // expected-error {{attempting to use the forward class 'SUPER' as superclass of 'INTF'}} @end @interface SUPER @end @@ -13,7 +13,7 @@ @interface INTF2 : INTF1 @end -@interface INTF3 : Y // expected-error {{cannot find interface declaration for 'Y', superclass of 'INTF3'}} \ +@interface INTF3 : Y // expected-error {{attempting to use the forward class 'Y' as superclass of 'INTF3'}} \ // expected-note{{'INTF3' declared here}} @end diff --git a/test/SemaObjC/warn-retain-cycle.m b/test/SemaObjC/warn-retain-cycle.m new file mode 100644 index 000000000000..71385b8400bb --- /dev/null +++ b/test/SemaObjC/warn-retain-cycle.m @@ -0,0 +1,91 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fobjc-arc -fblocks -verify %s + +@interface Test0 +- (void) setBlock: (void(^)(void)) block; +- (void) addBlock: (void(^)(void)) block; +- (void) actNow; +@end +void test0(Test0 *x) { + [x setBlock: // expected-note {{block will be retained by the captured object}} + ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} + x.block = // expected-note {{block will be retained by the captured object}} + ^{ [x actNow]; }; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} + + [x addBlock: // expected-note {{block will be retained by the captured object}} + ^{ [x actNow]; }]; // expected-warning {{capturing 'x' strongly in this block is likely to lead to a retain cycle}} + + // These actually don't cause retain cycles. + __weak Test0 *weakx = x; + [x addBlock: ^{ [weakx actNow]; }]; + [x setBlock: ^{ [weakx actNow]; }]; + x.block = ^{ [weakx actNow]; }; + + // These do cause retain cycles, but we're not clever enough to figure that out. + [weakx addBlock: ^{ [x actNow]; }]; + [weakx setBlock: ^{ [x actNow]; }]; + weakx.block = ^{ [x actNow]; }; +} + +@interface BlockOwner +@property (retain) void (^strong)(void); +@end + +@interface Test1 { +@public + BlockOwner *owner; +}; +@property (retain) BlockOwner *owner; +@property (assign) __strong BlockOwner *owner2; // expected-error {{unsafe_unretained property 'owner2' may not also be declared __strong}} +@property (assign) BlockOwner *owner3; +@end +void test1(Test1 *x) { + x->owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + x.owner.strong = ^{ (void) x; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + x.owner2.strong = ^{ (void) x; }; + x.owner3.strong = ^{ (void) x; }; +} + +@implementation Test1 { + BlockOwner * __unsafe_unretained owner3ivar; + __weak BlockOwner *weakowner; +} +@dynamic owner; +@dynamic owner2; +@synthesize owner3 = owner3ivar; + +- (id) init { + self.owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + self.owner2.strong = ^{ (void) owner; }; + + // TODO: should we warn here? What's the story with this kind of mismatch? + self.owner3.strong = ^{ (void) owner; }; + + owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + + owner.strong = ^{ ^{ (void) owner; }(); }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} + + owner.strong = ^{ (void) sizeof(self); // expected-note {{block will be retained by an object strongly retained by the captured object}} + (void) owner; }; // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} + + weakowner.strong = ^{ (void) owner; }; + + return self; +} +- (void) foo { + owner.strong = ^{ (void) owner; }; // expected-warning {{retain cycle}} expected-note {{block will be retained by an object strongly retained by the captured object}} +} +@end + +void test2_helper(id); +@interface Test2 { + void (^block)(void); + id x; +} +@end +@implementation Test2 +- (void) test { + block = ^{ // expected-note {{block will be retained by an object strongly retained by the captured object}} + test2_helper(x); // expected-warning {{capturing 'self' strongly in this block is likely to lead to a retain cycle}} + }; +} +@end diff --git a/test/SemaObjC/weak-property.m b/test/SemaObjC/weak-property.m new file mode 100644 index 000000000000..f0006076413d --- /dev/null +++ b/test/SemaObjC/weak-property.m @@ -0,0 +1,24 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fobjc-arc -verify %s +// rdar://8899430 + +@interface WeakPropertyTest { + Class isa; + __weak id value; + id x; +} +@property (weak) id value1; +@property __weak id value; +@property () __weak id value2; + +@property (weak, assign) id v1; // expected-error {{property attributes 'assign' and 'weak' are mutually exclusive}} +@property (weak, copy) id v2; // expected-error {{property attributes 'copy' and 'weak' are mutually exclusive}} +@property (weak, retain) id v3; // expected-error {{property attributes 'retain' and 'weak' are mutually exclusive}} +@property (weak, assign) id v4; // expected-error {{property attributes 'assign' and 'weak' are mutually exclusive}} + +@property () __weak id x; // expected-note {{property declared here}} +@end + +@implementation WeakPropertyTest +@synthesize x; // expected-error {{existing ivar 'x' for __weak property 'x' must be __weak}} +@dynamic value1, value, value2, v1,v2,v3,v4; +@end diff --git a/test/SemaObjCXX/Inputs/arc-system-header.h b/test/SemaObjCXX/Inputs/arc-system-header.h new file mode 100644 index 000000000000..d7adeb4d4008 --- /dev/null +++ b/test/SemaObjCXX/Inputs/arc-system-header.h @@ -0,0 +1,14 @@ +@interface B +@end + + +@interface A { +@public + union { + struct { + B *b; + } a_b; + void *void_ptr; + } data; +} +@end diff --git a/test/SemaObjCXX/arc-0x.mm b/test/SemaObjCXX/arc-0x.mm new file mode 100644 index 000000000000..fa022af522e9 --- /dev/null +++ b/test/SemaObjCXX/arc-0x.mm @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c++0x -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks -fobjc-exceptions %s + +// "Move" semantics, trivial version. +void move_it(__strong id &&from) { + id to = static_cast<__strong id&&>(from); +} + +// Deduction with 'auto'. +@interface A ++ alloc; +- init; +@end + +// Ensure that deduction works with lifetime qualifiers. +void deduction(id obj) { + auto a = [[A alloc] init]; + __strong A** aPtr = &a; + + auto a2([[A alloc] init]); + __strong A** aPtr2 = &a2; + + __strong id *idp = new auto(obj); + + __strong id array[17]; + for (auto x : array) { + __strong id *xPtr = &x; + } + + @try { + } @catch (auto e) { // expected-error {{'auto' not allowed in exception declaration}} + } +} diff --git a/test/SemaObjCXX/arc-bool-conversion.mm b/test/SemaObjCXX/arc-bool-conversion.mm new file mode 100644 index 000000000000..86da3ca90cf1 --- /dev/null +++ b/test/SemaObjCXX/arc-bool-conversion.mm @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s +// rdar://9310049 + +bool fn(id obj) { + return (bool)obj; +} + diff --git a/test/SemaObjCXX/arc-bridged-cast.mm b/test/SemaObjCXX/arc-bridged-cast.mm new file mode 100644 index 000000000000..cbbe79eb2744 --- /dev/null +++ b/test/SemaObjCXX/arc-bridged-cast.mm @@ -0,0 +1,36 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -fblocks -verify %s + +typedef const void *CFTypeRef; +typedef const struct __CFString *CFStringRef; + +@interface NSString +@end + +CFTypeRef CFCreateSomething(); +CFStringRef CFCreateString(); +CFTypeRef CFGetSomething(); +CFStringRef CFGetString(); + +id CreateSomething(); +NSString *CreateNSString(); + +template<typename IdType, typename StringType, typename IntPtrType> +void from_cf() { + id obj1 = (__bridge_transfer IdType)CFCreateSomething(); + id obj2 = (__bridge_transfer StringType)CFCreateString(); + (__bridge IntPtrType)CFCreateSomething(); // expected-error{{incompatible types casting 'CFTypeRef' (aka 'const void *') to 'int *' with a __bridge cast}} + id obj3 = (__bridge IdType)CFGetSomething(); + id obj4 = (__bridge StringType)CFGetString(); +} + +template void from_cf<id, NSString*, int*>(); // expected-note{{in instantiation of function template specialization}} + +template<typename IdType, typename StringType> +void to_cf(id obj) { + CFTypeRef cf1 = (__bridge_retained IdType)CreateSomething(); + CFStringRef cf2 = (__bridge_retained StringType)CreateNSString(); + CFTypeRef cf3 = (__bridge IdType)CreateSomething(); + CFStringRef cf4 = (__bridge StringType)CreateNSString(); +} + +template void to_cf<CFTypeRef, CFStringRef>(id); diff --git a/test/SemaObjCXX/arc-libcxx.mm b/test/SemaObjCXX/arc-libcxx.mm new file mode 100644 index 000000000000..7992f602f51a --- /dev/null +++ b/test/SemaObjCXX/arc-libcxx.mm @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-arc-cxxlib=libc++ -fobjc-nonfragile-abi -fobjc-runtime-has-weak -verify %s + +@interface A @end + +void f(__strong id &sir, __weak id &wir, __autoreleasing id &air, + __unsafe_unretained id &uir) { + __strong id *sip = std::addressof(sir); + __weak id *wip = std::addressof(wir); + __autoreleasing id *aip = std::addressof(air); + __unsafe_unretained id *uip = std::addressof(uir); +} diff --git a/test/SemaObjCXX/arc-libstdcxx.mm b/test/SemaObjCXX/arc-libstdcxx.mm new file mode 100644 index 000000000000..edb7a9ef486a --- /dev/null +++ b/test/SemaObjCXX/arc-libstdcxx.mm @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-arc-cxxlib=libstdc++ -fobjc-nonfragile-abi -fobjc-runtime-has-weak -verify %s + +@interface A @end + +int check0[std::__is_scalar<__strong id>::__value? -1 : 1]; +int check1[std::__is_scalar<__weak id>::__value? -1 : 1]; +int check2[std::__is_scalar<__autoreleasing id>::__value? -1 : 1]; +int check3[std::__is_scalar<__strong A*>::__value? -1 : 1]; +int check4[std::__is_scalar<__weak A*>::__value? -1 : 1]; +int check5[std::__is_scalar<__autoreleasing A*>::__value? -1 : 1]; diff --git a/test/SemaObjCXX/arc-memfunc.mm b/test/SemaObjCXX/arc-memfunc.mm new file mode 100644 index 000000000000..75b94c64e727 --- /dev/null +++ b/test/SemaObjCXX/arc-memfunc.mm @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify -fblocks %s + +struct X0 { + static id makeObject1() __attribute__((ns_returns_retained)); + id makeObject2() __attribute__((ns_returns_retained)); +}; + +void test_X0(X0 x0, X0 *x0p) { + X0::makeObject1(); + x0.makeObject2(); + x0p->makeObject2(); + id (X0::*pmf)() __attribute__((ns_returns_retained)) = &X0::makeObject2; + (x0.*pmf)(); + (x0p->*pmf)(); +} diff --git a/test/SemaObjCXX/arc-non-pod.mm b/test/SemaObjCXX/arc-non-pod.mm new file mode 100644 index 000000000000..6a47b3d85664 --- /dev/null +++ b/test/SemaObjCXX/arc-non-pod.mm @@ -0,0 +1,116 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -Warc-abi -verify -fblocks -triple x86_64-apple-darwin10.0.0 %s + +// Classes that have an Objective-C object pointer. +struct HasObjectMember0 { // expected-warning{{'HasObjectMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x; +}; + +struct HasObjectMember1 { // expected-warning{{'HasObjectMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x[3]; +}; + +struct HasObjectMember2 { // expected-warning{{'HasObjectMember2' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + id x[3][2]; +}; + +// Don't complain if the type has non-external linkage +namespace { + struct HasObjectMember3 { + id x[3][2]; + }; +} + +// Don't complain if the Objective-C pointer type was explicitly given +// no ownership. +struct HasObjectMember3 { + __unsafe_unretained id x[3][2]; +}; + +struct HasBlockPointerMember0 { // expected-warning{{'HasBlockPointerMember0' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + int (^bp)(int); +}; + +struct HasBlockPointerMember1 { // expected-warning{{'HasBlockPointerMember1' cannot be shared between ARC and non-ARC code; add a copy constructor, a copy assignment operator, and a destructor to make it ABI-compatible}} + int (^bp[2][3])(int); +}; + +struct NonPOD { + NonPOD(const NonPOD&); +}; + +struct HasObjectMemberAndNonPOD0 { // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + id x; + NonPOD np; +}; + +struct HasObjectMemberAndNonPOD1 { // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + id x[3]; +}; + +struct HasObjectMemberAndNonPOD2 { // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ + // expected-warning{{'HasObjectMemberAndNonPOD2' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + id x[3][2]; +}; + +struct HasObjectMemberAndNonPOD3 { + HasObjectMemberAndNonPOD3 &operator=(const HasObjectMemberAndNonPOD3&); + ~HasObjectMemberAndNonPOD3(); + NonPOD np; + id x[3][2]; +}; + +struct HasBlockPointerMemberAndNonPOD0 { // expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ +// expected-warning{{'HasBlockPointerMemberAndNonPOD0' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + int (^bp)(int); +}; + +struct HasBlockPointerMemberAndNonPOD1 { // expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial copy assignment operator to make it ABI-compatible}} \ +// expected-warning{{'HasBlockPointerMemberAndNonPOD1' cannot be shared between ARC and non-ARC code; add a non-trivial destructor to make it ABI-compatible}} + NonPOD np; + int (^bp[2][3])(int); +}; + +int check_non_pod_objc_pointer0[__is_pod(id)? 1 : -1]; +int check_non_pod_objc_pointer1[__is_pod(__strong id)? -1 : 1]; +int check_non_pod_objc_pointer2[__is_pod(__unsafe_unretained id)? 1 : -1]; +int check_non_pod_objc_pointer3[__is_pod(id[2][3])? 1 : -1]; +int check_non_pod_objc_pointer4[__is_pod(__unsafe_unretained id[2][3])? 1 : -1]; +int check_non_pod_block0[__is_pod(int (^)(int))? 1 : -1]; +int check_non_pod_block1[__is_pod(int (^ __unsafe_unretained)(int))? 1 : -1]; +int check_non_pod_block2[__is_pod(int (^ __strong)(int))? -1 : 1]; + +struct FlexibleArrayMember0 { + int length; + id array[]; // expected-error{{flexible array member 'array' of non-POD element type 'id __strong[]'}} +}; + +struct FlexibleArrayMember1 { + int length; + __unsafe_unretained id array[]; +}; + +// It's okay to pass a retainable type through an ellipsis. +void variadic(...); +void test_variadic() { + variadic(1, 17, @"Foo"); +} + +// It's okay to create a VLA of retainable types. +void vla(int n) { + id vla[n]; +} + +@interface Crufty { + union { + struct { + id object; // expected-note{{has __strong ownership}} + } an_object; // expected-error{{union member 'an_object' has a non-trivial copy constructor}} + void *ptr; + } storage; +} +@end diff --git a/test/SemaObjCXX/arc-object-init-destroy.mm b/test/SemaObjCXX/arc-object-init-destroy.mm new file mode 100644 index 000000000000..196f49371847 --- /dev/null +++ b/test/SemaObjCXX/arc-object-init-destroy.mm @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -Warc-abi -fblocks -triple x86_64-apple-darwin10.0.0 %s + +typedef __strong id strong_id; +typedef __weak id weak_id; +void test_pseudo_destructors(__strong id *sptr, __weak id *wptr) { + sptr->~id(); // okay + wptr->~id(); // okay + sptr->~strong_id(); // okay + wptr->~weak_id(); + sptr->~weak_id(); // expected-error{{pseudo-destructor destroys object of type '__strong id' with inconsistently-qualified type 'weak_id' (aka '__weak id')}} + wptr->strong_id::~strong_id(); // expected-error{{pseudo-destructor destroys object of type '__weak id' with inconsistently-qualified type 'strong_id' (aka '__strong id')}} + + sptr->id::~id(); // okay + wptr->id::~id(); // okay +} + +void test_delete(__strong id *sptr, __weak id *wptr) { + delete sptr; + delete wptr; + delete [] sptr; // expected-warning{{destroying an array of '__strong id'; this array must not have been allocated from non-ARC code}} + delete [] wptr; // expected-warning{{destroying an array of '__weak id'; this array must not have been allocated from non-ARC code}} +} + +void test_new(int n) { + (void)new strong_id; + (void)new weak_id; + (void)new strong_id [n]; // expected-warning{{allocating an array of 'strong_id' (aka '__strong id'); this array must not be deleted in non-ARC code}} + (void)new weak_id [n]; // expected-warning{{allocating an array of 'weak_id' (aka '__weak id'); this array must not be deleted in non-ARC code}} + + (void)new __strong id; + (void)new __weak id; + (void)new __strong id [n]; // expected-warning{{allocating an array of '__strong id'; this array must not be deleted in non-ARC code}} + + // Infer '__strong'. + __strong id *idptr = new id; + __strong id *idptr2 = new id [n]; // expected-warning{{allocating an array of '__strong id'; this array must not be deleted in non-ARC code}} + + // ... but not for arrays. + typedef id id_array[2][3]; + (void)new id_array; // expected-error{{'new' cannot allocate an array of 'id' with no explicit ownership}} + + typedef __strong id strong_id_array[2][3]; + typedef __strong id strong_id_3[3]; + strong_id_3 *idptr3 = new strong_id_array; // expected-warning{{allocating an array of '__strong id'; this array must not be deleted in non-ARC code}} +} + +void test_jump_scope() { + goto done; // expected-error{{goto into protected scope}} + __strong id x; // expected-note{{jump bypasses initialization of retaining variable}} + done: + return; +} diff --git a/test/SemaObjCXX/arc-overloading.mm b/test/SemaObjCXX/arc-overloading.mm new file mode 100644 index 000000000000..06b332cec85f --- /dev/null +++ b/test/SemaObjCXX/arc-overloading.mm @@ -0,0 +1,175 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks %s + +// Simple ownership conversions + diagnostics. +int &f0(id __strong const *); // expected-note{{candidate function not viable: 1st argument ('__weak id *') has __weak ownership, but parameter has __strong ownership}} + +void test_f0() { + id __strong *sip; + id __strong const *csip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + int &ir1 = f0(sip); + int &ir2 = f0(csip); + int &ir3 = f0(aip); + int &ir4 = f0(uip); + f0(wip); // expected-error{{no matching function for call to 'f0'}} +} + +// Simple overloading +int &f1(id __strong const *); +float &f1(id __weak const *); + +void test_f1() { + id __strong *sip; + id __strong const *csip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + int &ir1 = f1(sip); + int &ir2 = f1(csip); + float &fr1 = f1(wip); + int &ir3 = f1(aip); + int &ir4 = f1(uip); +} + +// Simple overloading +int &f2(id __strong const *); // expected-note{{candidate function}} +float &f2(id __autoreleasing const *); // expected-note{{candidate function}} + +void test_f2() { + id __strong *sip; + id __strong const *csip; + id __weak *wip; + id __autoreleasing *aip; + id __unsafe_unretained *uip; + + // Prefer non-ownership conversions to ownership conversions. + int &ir1 = f2(sip); + int &ir2 = f2(csip); + float &fr1 = f2(aip); + + f2(uip); // expected-error{{call to 'f2' is ambiguous}} +} + +// Writeback conversion +int &f3(id __autoreleasing *); // expected-note{{candidate function not viable: 1st argument ('__unsafe_unretained id *') has __unsafe_unretained ownership, but parameter has __autoreleasing ownership}} + +void test_f3() { + id __strong sip; + id __weak wip; + id __autoreleasing aip; + id __unsafe_unretained uip; + + int &ir1 = f3(&sip); + int &ir2 = f3(&wip); + int &ir3 = f3(&aip); + f3(&uip); // expected-error{{no matching function for call to 'f3'}} +} + +// Writeback conversion vs. no conversion +int &f4(id __autoreleasing *); +float &f4(id __strong *); + +void test_f4() { + id __strong sip; + id __weak wip; + id __autoreleasing aip; + extern __weak id weak_global_ptr; + + float &fr1 = f4(&sip); + int &ir1 = f4(&wip); + int &ir2 = f4(&aip); + int &ir3 = f4(&weak_global_ptr); // expected-error{{passing address of non-local object to __autoreleasing parameter for write-back}} +} + +// Writeback conversion vs. other conversion. +int &f5(id __autoreleasing *); +float &f5(id const __unsafe_unretained *); + +void test_f5() { + id __strong sip; + id __weak wip; + id __autoreleasing aip; + + int &ir1 = f5(&wip); + float &fr1 = f5(&sip); + int &ir2 = f5(&aip); +} + +@interface A +@end + +int &f6(id __autoreleasing *); +float &f6(id const __unsafe_unretained *); + +void test_f6() { + A* __strong sip; + A* __weak wip; + A* __autoreleasing aip; + + int &ir1 = f6(&wip); + float &fr1 = f6(&sip); + int &ir2 = f6(&aip); +} + +// Reference binding +void f7(__strong id&); // expected-note{{candidate function not viable: 1st argument ('__weak id') has __weak ownership, but parameter has __strong ownership}} \ + // expected-note{{candidate function not viable: 1st argument ('__autoreleasing id') has __autoreleasing ownership, but parameter has __strong ownership}} \ + // expected-note{{candidate function not viable: 1st argument ('__unsafe_unretained id') has __unsafe_unretained ownership, but parameter has __strong ownership}} + +void test_f7() { + __strong id strong_id; + __weak id weak_id; + __autoreleasing id autoreleasing_id; + __unsafe_unretained id unsafe_id; + f7(strong_id); + f7(weak_id); // expected-error{{no matching function for call to 'f7'}} + f7(autoreleasing_id); // expected-error{{no matching function for call to 'f7'}} + f7(unsafe_id); // expected-error{{no matching function for call to 'f7'}} +} + +void f8(const __strong id&); + +void test_f8() { + __strong id strong_id; + __weak id weak_id; + __autoreleasing id autoreleasing_id; + __unsafe_unretained id unsafe_id; + + f8(strong_id); + f8(weak_id); + f8(autoreleasing_id); + f8(unsafe_id); +} + +int &f9(__strong id&); +float &f9(const __autoreleasing id&); + +void test_f9() { + __strong id strong_id; + __weak id weak_id; + __autoreleasing id autoreleasing_id; + __unsafe_unretained id unsafe_id; + + int &ir1 = f9(strong_id); + float &fr1 = f9(autoreleasing_id); + float &fr2 = f9(unsafe_id); + float &fr2a = f9(weak_id); + + __strong A *strong_a; + __weak A *weak_a; + __autoreleasing A *autoreleasing_a; + __unsafe_unretained A *unsafe_unretained_a; + float &fr3 = f9(strong_a); + float &fr4 = f9(autoreleasing_a); + float &fr5 = f9(unsafe_unretained_a); + float &fr6 = f9(weak_a); + + const __autoreleasing id& ar1 = strong_a; + const __autoreleasing id& ar2 = autoreleasing_a; + const __autoreleasing id& ar3 = unsafe_unretained_a; + const __autoreleasing id& ar4 = weak_a; +} diff --git a/test/SemaObjCXX/arc-system-header.mm b/test/SemaObjCXX/arc-system-header.mm new file mode 100644 index 000000000000..cb2b85868ce1 --- /dev/null +++ b/test/SemaObjCXX/arc-system-header.mm @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fobjc-arc -fobjc-nonfragile-abi -isystem %S/Inputs %s -verify + +#include <arc-system-header.h> + +void f(A* a) { + a->data.void_ptr = 0; + a->data.a_b.b = 0; // expected-error{{'a_b' is unavailable: this system field has retaining ownership}} +} +// Silly location below +// expected-note{{declaration has been explicitly marked unavailable here}} diff --git a/test/SemaObjCXX/arc-templates.mm b/test/SemaObjCXX/arc-templates.mm new file mode 100644 index 000000000000..fa4e0a777642 --- /dev/null +++ b/test/SemaObjCXX/arc-templates.mm @@ -0,0 +1,254 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks %s + +@interface A +@end + +template<typename T, typename U> +struct is_same { + static const bool value = false; +}; + +template<typename T> +struct is_same<T, T> { + static const bool value = true; +}; + +// Instantiation for reference/pointer types that will get lifetime +// adjustments. +template<typename T> +struct X0 { + typedef T* pointer; // okay: ends up being strong. + typedef T& reference; // okay: ends up being strong +}; + +void test_X0() { + X0<id> x0id; + X0<A*> x0a; + X0<__strong A*> x0sa; + + id __strong *ptr; + id __strong val; + X0<__strong id>::pointer &ptr_ref = ptr; + X0<__strong id>::reference ref = val; +} + +int check_infer_strong[is_same<id, __strong id>::value? 1 : -1]; + +// Check template argument deduction (e.g., for specialization) using +// lifetime qualifiers. +template<typename T> +struct is_pointer_strong { + static const bool value = false; +}; + +template<typename T> +struct is_pointer_strong<__strong T*> { + static const bool value = true; +}; + +int check_ptr_strong1[is_pointer_strong<__strong id*>::value? 1 : -1]; +int check_ptr_strong2[is_pointer_strong<__weak id*>::value? -1 : 1]; +int check_ptr_strong3[is_pointer_strong<__autoreleasing id*>::value? -1 : 1]; +int check_ptr_strong4[is_pointer_strong<__unsafe_unretained id*>::value? -1 : 1]; +int check_ptr_strong5[is_pointer_strong<id>::value? -1 : 1]; + +// Check substitution into lifetime-qualified dependent types. +template<typename T> +struct make_strong_pointer { + typedef __strong T *type; +}; + +template<typename T> +struct make_strong_pointer<__weak T> { + typedef __strong T *type; +}; + +template<typename T> +struct make_strong_pointer<__autoreleasing T> { + typedef __strong T *type; +}; + +template<typename T> +struct make_strong_pointer<__unsafe_unretained T> { + typedef __strong T *type; +}; + +// Adding qualifiers +int check_make_strong1[is_same<make_strong_pointer<id>::type, __strong id *>::value ? 1 : -1]; +int check_make_strong2[is_same<make_strong_pointer<A*>::type, A* __strong *>::value ? 1 : -1]; + +// Adding redundant qualifiers +int check_make_strong3[is_same<make_strong_pointer<__strong id>::type, __strong id *>::value ? 1 : -1]; +int check_make_strong4[is_same<make_strong_pointer<__strong A*>::type, A* __strong *>::value ? 1 : -1]; + +// Adding nonsensical qualifiers. +int check_make_strong5[is_same<make_strong_pointer<int>::type, int *>::value ? 1 : -1]; +int check_make_strong6[is_same<make_strong_pointer<__weak id>::type, __strong id *>::value ? 1 : -1]; + +template<typename T> +struct make_weak { + typedef __weak T type; +}; + +int check_make_weak0[is_same<make_weak<id>::type, __weak id>::value? 1 : -1]; +int check_make_weak1[is_same<make_weak<__strong id>::type, __weak id>::value? 1 : -1]; +int check_make_weak2[is_same<make_weak<__autoreleasing id>::type, __weak id>::value? 1 : -1]; + +template<typename T> +struct make_weak_fail { + typedef T T_type; + typedef __weak T_type type; // expected-error{{the type 'T_type' (aka '__weak id') already has retainment attributes set on it}} \ + // expected-error{{the type 'T_type' (aka '__strong id') already has retainment attributes set on it}} +}; + +int check_make_weak_fail0[is_same<make_weak_fail<__weak id>::type, __weak id>::value? 1 : -1]; // expected-note{{in instantiation of template class 'make_weak_fail<__weak id>' requested here}} + +int check_make_weak_fail1[is_same<make_weak_fail<id>::type, __weak id>::value? -1 : 1]; // expected-note{{in instantiation of template class 'make_weak_fail<id>' requested here}} + +// Check template argument deduction from function templates. +template<typename T> struct identity { }; + +template<typename T> identity<T> accept_strong_ptr(__strong T*); +template<typename T> identity<T> accept_strong_ref(__strong T&); + +template<typename T> identity<T> accept_any_ptr(T*); +template<typename T> identity<T> accept_any_ref(T&); + +void test_func_deduction_id() { + __strong id *sip; + __weak id *wip; + __autoreleasing id *aip; + __unsafe_unretained id *uip; + + identity<id> res1 = accept_strong_ptr(sip); + identity<__strong id> res2 = accept_any_ptr(sip); + + __strong id si; + __weak id wi; + __autoreleasing id ai; + __unsafe_unretained id ui; + identity<id> res3 = accept_strong_ref(si); + identity<__strong id> res4 = accept_any_ref(si); + identity<__weak id> res5 = accept_any_ref(wi); + identity<__autoreleasing id> res6 = accept_any_ref(ai); + identity<__unsafe_unretained id> res7 = accept_any_ref(ui); +} + +void test_func_deduction_A() { + __strong A * *sip; + __weak A * *wip; + __autoreleasing A * *aip; + __unsafe_unretained A * *uip; + + identity<A *> res1 = accept_strong_ptr(sip); + identity<__strong A *> res2 = accept_any_ptr(sip); + + __strong A * si; + __weak A * wi; + __autoreleasing A * ai; + __unsafe_unretained A * ui; + identity<A *> res3 = accept_strong_ref(si); + identity<__strong A *> res4 = accept_any_ref(si); + identity<__weak A *> res5 = accept_any_ref(wi); + identity<__autoreleasing A *> res6 = accept_any_ref(ai); + identity<__unsafe_unretained A *> res7 = accept_any_ref(ui); +} + +// Test partial ordering (qualified vs. non-qualified). +template<typename T> +struct classify_pointer_pointer { + static const unsigned value = 0; +}; + +template<typename T> +struct classify_pointer_pointer<T*> { + static const unsigned value = 1; +}; + +template<typename T> +struct classify_pointer_pointer<__strong T*> { + static const unsigned value = 2; +}; + +template<typename T> +struct classify_pointer_pointer<__weak T*> { + static const unsigned value = 3; +}; + +template<typename T> +struct classify_pointer_pointer<T&> { + static const unsigned value = 4; +}; + +template<typename T> +struct classify_pointer_pointer<__strong T&> { + static const unsigned value = 5; +}; + +template<typename T> +struct classify_pointer_pointer<__weak T&> { + static const unsigned value = 6; +}; + +int classify_ptr1[classify_pointer_pointer<int>::value == 0? 1 : -1]; +int classify_ptr2[classify_pointer_pointer<int *>::value == 1? 1 : -1]; +int classify_ptr3[classify_pointer_pointer<id __strong *>::value == 2? 1 : -1]; +int classify_ptr4[classify_pointer_pointer<id __weak *>::value == 3? 1 : -1]; +int classify_ptr5[classify_pointer_pointer<int&>::value == 4? 1 : -1]; +int classify_ptr6[classify_pointer_pointer<id __strong&>::value == 5? 1 : -1]; +int classify_ptr7[classify_pointer_pointer<id __weak&>::value == 6? 1 : -1]; +int classify_ptr8[classify_pointer_pointer<id __autoreleasing&>::value == 4? 1 : -1]; +int classify_ptr9[classify_pointer_pointer<id __unsafe_unretained&>::value == 4? 1 : -1]; +int classify_ptr10[classify_pointer_pointer<id __autoreleasing *>::value == 1? 1 : -1]; +int classify_ptr11[classify_pointer_pointer<id __unsafe_unretained *>::value == 1? 1 : -1]; +int classify_ptr12[classify_pointer_pointer<int *>::value == 1? 1 : -1]; +int classify_ptr13[classify_pointer_pointer<A * __strong *>::value == 2? 1 : -1]; +int classify_ptr14[classify_pointer_pointer<A * __weak *>::value == 3? 1 : -1]; +int classify_ptr15[classify_pointer_pointer<int&>::value == 4? 1 : -1]; +int classify_ptr16[classify_pointer_pointer<A * __strong&>::value == 5? 1 : -1]; +int classify_ptr17[classify_pointer_pointer<A * __weak&>::value == 6? 1 : -1]; +int classify_ptr18[classify_pointer_pointer<A * __autoreleasing&>::value == 4? 1 : -1]; +int classify_ptr19[classify_pointer_pointer<A * __unsafe_unretained&>::value == 4? 1 : -1]; +int classify_ptr20[classify_pointer_pointer<A * __autoreleasing *>::value == 1? 1 : -1]; +int classify_ptr21[classify_pointer_pointer<A * __unsafe_unretained *>::value == 1? 1 : -1]; + +template<typename T> int& qual_vs_unqual_ptr(__strong T*); +template<typename T> double& qual_vs_unqual_ptr(__weak T*); +template<typename T> float& qual_vs_unqual_ptr(T*); +template<typename T> int& qual_vs_unqual_ref(__strong T&); +template<typename T> double& qual_vs_unqual_ref(__weak T&); +template<typename T> float& qual_vs_unqual_ref(T&); + +void test_qual_vs_unqual_id() { + __strong id *sip; + __weak id *wip; + __autoreleasing id *aip; + __unsafe_unretained id *uip; + + int &ir1 = qual_vs_unqual_ptr(sip); + double &dr1 = qual_vs_unqual_ptr(wip); + float &fr1 = qual_vs_unqual_ptr(aip); + float &fr2 = qual_vs_unqual_ptr(uip); + + int &ir2 = qual_vs_unqual_ref(*sip); + double &dr2 = qual_vs_unqual_ref(*wip); + float &fr3 = qual_vs_unqual_ref(*aip); + float &fr4 = qual_vs_unqual_ref(*uip); +} + +void test_qual_vs_unqual_a() { + __strong A * *sap; + __weak A * *wap; + __autoreleasing A * *aap; + __unsafe_unretained A * *uap; + + int &ir1 = qual_vs_unqual_ptr(sap); + double &dr1 = qual_vs_unqual_ptr(wap); + float &fr1 = qual_vs_unqual_ptr(aap); + float &fr2 = qual_vs_unqual_ptr(uap); + + int &ir2 = qual_vs_unqual_ref(*sap); + double &dr2 = qual_vs_unqual_ref(*wap); + float &fr3 = qual_vs_unqual_ref(*aap); + float &fr4 = qual_vs_unqual_ref(*uap); +} diff --git a/test/SemaObjCXX/arc-type-conversion.mm b/test/SemaObjCXX/arc-type-conversion.mm new file mode 100644 index 000000000000..f52f54a50cc4 --- /dev/null +++ b/test/SemaObjCXX/arc-type-conversion.mm @@ -0,0 +1,221 @@ +// RUN: %clang_cc1 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify -fblocks %s +// rdar://8843600 + +void * cvt(id arg) // expected-note{{candidate function not viable: cannot convert argument of incomplete type 'void *' to '__strong id'}} +{ + void* voidp_val; + (void)(int*)arg; // expected-error {{cast of an Objective-C pointer to 'int *' is disallowed with ARC}} + (void)(id)arg; + (void)(__autoreleasing id*)arg; // expected-error{{C-style cast from 'id' to '__autoreleasing id *' casts away qualifiers}} + (void)(id*)arg; // expected-error{{C-style cast from 'id' to '__strong id *' casts away qualifiers}} + + (void)(__autoreleasing id**)voidp_val; + (void)(void*)voidp_val; + (void)(void**)arg; // expected-error {{cast of an Objective-C pointer to 'void **' is disallowed}} + cvt((void*)arg); // expected-error {{cast of Objective-C pointer type 'id' to C pointer type 'void *' requires a bridged cast}} \ + // expected-error {{no matching function for call to 'cvt'}} \ + // expected-note{{use __bridge to convert directly (no change in ownership)}} \ + // expected-note{{use __bridge_retained to make an ARC object available as a +1 'void *'}} + cvt(0); + (void)(__strong id**)(0); + + // FIXME: Diagnostic could be better here. + return arg; // expected-error{{cannot initialize return object of type 'void *' with an lvalue of type '__strong id'}} +} + +// rdar://8898937 +namespace rdar8898937 { + +typedef void (^dispatch_block_t)(void); + +void dispatch_once(dispatch_block_t block); +static void _dispatch_once(dispatch_block_t block) +{ + dispatch_once(block); +} + +} + +void static_casts(id arg) { + void* voidp_val; + (void)static_cast<int*>(arg); // expected-error {{cannot cast from type 'id' to pointer type 'int *'}} + (void)static_cast<id>(arg); + (void)static_cast<__autoreleasing id*>(arg); // expected-error{{cannot cast from type 'id' to pointer type '__autoreleasing id *'}} + (void)static_cast<id*>(arg); // expected-error {{cannot cast from type 'id' to pointer type '__strong id *'}} + + (void)static_cast<__autoreleasing id**>(voidp_val); + (void)static_cast<void*>(voidp_val); + (void)static_cast<void**>(arg); // expected-error {{cannot cast from type 'id' to pointer type 'void **'}} + (void)static_cast<__strong id**>(0); + + __strong id *idp; + (void)static_cast<__autoreleasing id*>(idp); // expected-error{{static_cast from '__strong id *' to '__autoreleasing id *' is not allowed}} + (void)static_cast<__weak id*>(idp); // expected-error{{static_cast from '__strong id *' to '__weak id *' is not allowed}} +} + +void test_const_cast(__strong id *sip, __weak id *wip, + const __strong id *csip, __weak const id *cwip) { + // Cannot use const_cast to cast between ownership qualifications or + // add/remove ownership qualifications. + (void)const_cast<__strong id *>(wip); // expected-error{{is not allowed}} + (void)const_cast<__weak id *>(sip); // expected-error{{is not allowed}} + + // It's acceptable to cast away constness. + (void)const_cast<__strong id *>(csip); + (void)const_cast<__weak id *>(cwip); +} + +void test_reinterpret_cast(__strong id *sip, __weak id *wip, + const __strong id *csip, __weak const id *cwip) { + // Okay to reinterpret_cast to add/remove/change ownership + // qualifications. + (void)reinterpret_cast<__strong id *>(wip); + (void)reinterpret_cast<__weak id *>(sip); + + // Not allowed to cast away constness + (void)reinterpret_cast<__strong id *>(csip); // expected-error{{reinterpret_cast from '__strong id const *' to '__strong id *' casts away qualifiers}} + (void)reinterpret_cast<__weak id *>(cwip); // expected-error{{reinterpret_cast from '__weak id const *' to '__weak id *' casts away qualifiers}} + (void)reinterpret_cast<__weak id *>(csip); // expected-error{{reinterpret_cast from '__strong id const *' to '__weak id *' casts away qualifiers}} + (void)reinterpret_cast<__strong id *>(cwip); // expected-error{{reinterpret_cast from '__weak id const *' to '__strong id *' casts away qualifiers}} +} + +void test_cstyle_cast(__strong id *sip, __weak id *wip, + const __strong id *csip, __weak const id *cwip) { + // C-style casts aren't allowed to change Objective-C ownership + // qualifiers (beyond what the normal implicit conversion allows). + + (void)(__strong id *)wip; // expected-error{{C-style cast from '__weak id *' to '__strong id *' casts away qualifiers}} + (void)(__strong id *)cwip; // expected-error{{C-style cast from '__weak id const *' to '__strong id *' casts away qualifiers}} + (void)(__weak id *)sip; // expected-error{{C-style cast from '__strong id *' to '__weak id *' casts away qualifiers}} + (void)(__weak id *)csip; // expected-error{{C-style cast from '__strong id const *' to '__weak id *' casts away qualifiers}} + + (void)(__strong const id *)wip; // expected-error{{C-style cast from '__weak id *' to '__strong id const *' casts away qualifiers}} + (void)(__strong const id *)cwip; // expected-error{{C-style cast from '__weak id const *' to '__strong id const *' casts away qualifiers}} + (void)(__weak const id *)sip; // expected-error{{C-style cast from '__strong id *' to '__weak id const *' casts away qualifiers}} + (void)(__weak const id *)csip; // expected-error{{C-style cast from '__strong id const *' to '__weak id const *' casts away qualifiers}} + (void)(__autoreleasing const id *)wip; // expected-error{{C-style cast from '__weak id *' to '__autoreleasing id const *' casts away qualifiers}} + (void)(__autoreleasing const id *)cwip; // expected-error{{C-style cast from '__weak id const *' to '__autoreleasing id const *' casts away qualifiers}} + (void)(__autoreleasing const id *)sip; + (void)(__autoreleasing const id *)csip; +} + +void test_functional_cast(__strong id *sip, __weak id *wip, + __autoreleasing id *aip) { + // Functional casts aren't allowed to change Objective-C ownership + // qualifiers (beyond what the normal implicit conversion allows). + + typedef __strong id *strong_id_pointer; + typedef __weak id *weak_id_pointer; + typedef __autoreleasing id *autoreleasing_id_pointer; + + typedef const __strong id *const_strong_id_pointer; + typedef const __weak id *const_weak_id_pointer; + typedef const __autoreleasing id *const_autoreleasing_id_pointer; + + (void)strong_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'strong_id_pointer' (aka '__strong id *') casts away qualifiers}} + (void)weak_id_pointer(sip); // expected-error{{functional-style cast from '__strong id *' to 'weak_id_pointer' (aka '__weak id *') casts away qualifiers}} + (void)autoreleasing_id_pointer(sip); // expected-error{{functional-style cast from '__strong id *' to 'autoreleasing_id_pointer' (aka '__autoreleasing id *') casts away qualifiers}} + (void)autoreleasing_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'autoreleasing_id_pointer' (aka '__autoreleasing id *') casts away qualifiers}} + (void)const_strong_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'const_strong_id_pointer' (aka 'const __strong id *') casts away qualifiers}} + (void)const_weak_id_pointer(sip); // expected-error{{functional-style cast from '__strong id *' to 'const_weak_id_pointer' (aka 'const __weak id *') casts away qualifiers}} + (void)const_autoreleasing_id_pointer(sip); + (void)const_autoreleasing_id_pointer(aip); + (void)const_autoreleasing_id_pointer(wip); // expected-error{{functional-style cast from '__weak id *' to 'const_autoreleasing_id_pointer' (aka 'const __autoreleasing id *') casts away qualifiers}} +} + +void test_unsafe_unretained(__strong id *sip, __weak id *wip, + __autoreleasing id *aip, + __unsafe_unretained id *uip, + const __unsafe_unretained id *cuip) { + uip = sip; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type '__strong id *'}} + uip = wip; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type '__weak id *'}} + uip = aip; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type '__autoreleasing id *'}} + + cuip = sip; + cuip = wip; // expected-error{{assigning to '__unsafe_unretained id const *' from incompatible type '__weak id *'}} + cuip = aip; +} + +void to_void(__strong id *sip, __weak id *wip, + __autoreleasing id *aip, + __unsafe_unretained id *uip) { + void *vp1 = sip; + void *vp2 = wip; + void *vp3 = aip; + void *vp4 = uip; + (void)(void*)sip; + (void)(void*)wip; + (void)(void*)aip; + (void)(void*)uip; + (void)static_cast<void*>(sip); + (void)static_cast<void*>(wip); + (void)static_cast<void*>(aip); + (void)static_cast<void*>(uip); + (void)reinterpret_cast<void*>(sip); + (void)reinterpret_cast<void*>(wip); + (void)reinterpret_cast<void*>(aip); + (void)reinterpret_cast<void*>(uip); + + (void)(void*)&sip; + (void)(void*)&wip; + (void)(void*)&aip; + (void)(void*)&uip; + (void)static_cast<void*>(&sip); + (void)static_cast<void*>(&wip); + (void)static_cast<void*>(&aip); + (void)static_cast<void*>(&uip); + (void)reinterpret_cast<void*>(&sip); + (void)reinterpret_cast<void*>(&wip); + (void)reinterpret_cast<void*>(&aip); + (void)reinterpret_cast<void*>(&uip); +} + +void from_void(void *vp) { + __strong id *sip = (__strong id *)vp; + __weak id *wip = (__weak id *)vp; + __autoreleasing id *aip = (__autoreleasing id *)vp; + __unsafe_unretained id *uip = (__unsafe_unretained id *)vp; + __strong id *sip2 = static_cast<__strong id *>(vp); + __weak id *wip2 = static_cast<__weak id *>(vp); + __autoreleasing id *aip2 = static_cast<__autoreleasing id *>(vp); + __unsafe_unretained id *uip2 = static_cast<__unsafe_unretained id *>(vp); + __strong id *sip3 = reinterpret_cast<__strong id *>(vp); + __weak id *wip3 = reinterpret_cast<__weak id *>(vp); + __autoreleasing id *aip3 = reinterpret_cast<__autoreleasing id *>(vp); + __unsafe_unretained id *uip3 = reinterpret_cast<__unsafe_unretained id *>(vp); + + __strong id **sipp = (__strong id **)vp; + __weak id **wipp = (__weak id **)vp; + __autoreleasing id **aipp = (__autoreleasing id **)vp; + __unsafe_unretained id **uipp = (__unsafe_unretained id **)vp; + + sip = vp; // expected-error{{assigning to '__strong id *' from incompatible type 'void *'}} + wip = vp; // expected-error{{assigning to '__weak id *' from incompatible type 'void *'}} + aip = vp; // expected-error{{assigning to '__autoreleasing id *' from incompatible type 'void *'}} + uip = vp; // expected-error{{assigning to '__unsafe_unretained id *' from incompatible type 'void *'}} +} + +typedef void (^Block)(); +typedef void (^Block_strong)() __strong; +typedef void (^Block_autoreleasing)() __autoreleasing; + +@class NSString; + +void ownership_transfer_in_cast(void *vp, Block *pblk) { + __strong NSString **sip2 = static_cast<NSString **>(static_cast<__strong id *>(vp)); + __strong NSString **&si2pref = static_cast<NSString **&>(sip2); + __weak NSString **wip2 = static_cast<NSString **>(static_cast<__weak id *>(vp)); + __autoreleasing id *aip2 = static_cast<id *>(static_cast<__autoreleasing id *>(vp)); + __unsafe_unretained id *uip2 = static_cast<id *>(static_cast<__unsafe_unretained id *>(vp)); + __strong id *sip3 = reinterpret_cast<id *>(reinterpret_cast<__strong id *>(vp)); + __weak id *wip3 = reinterpret_cast<id *>(reinterpret_cast<__weak id *>(vp)); + __autoreleasing id *aip3 = reinterpret_cast<id *>(reinterpret_cast<__autoreleasing id *>(vp)); + __unsafe_unretained id *uip3 = reinterpret_cast<id *>(reinterpret_cast<__unsafe_unretained id *>(vp)); + + Block_strong blk_strong1; + Block_strong blk_strong2 = static_cast<Block>(blk_strong1); + Block_autoreleasing *blk_auto = static_cast<Block*>(pblk); +} + +// Make sure we don't crash. +void writeback_test(NSString & &) {} // expected-error {{type name declared as a reference to a reference}} diff --git a/test/SemaObjCXX/arc-type-traits.mm b/test/SemaObjCXX/arc-type-traits.mm new file mode 100644 index 000000000000..f50904b03c73 --- /dev/null +++ b/test/SemaObjCXX/arc-type-traits.mm @@ -0,0 +1,90 @@ +// RUN: %clang_cc1 -fsyntax-only -fobjc-arc -fobjc-nonfragile-abi -fobjc-runtime-has-weak -verify %s + +// Check the results of the various type-trait query functions on +// lifetime-qualified types in ARC. + +#define JOIN3(X,Y) X ## Y +#define JOIN2(X,Y) JOIN3(X,Y) +#define JOIN(X,Y) JOIN2(X,Y) + +#define TRAIT_IS_TRUE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? 1 : -1] +#define TRAIT_IS_FALSE(Trait, Type) char JOIN2(Trait,__LINE__)[Trait(Type)? -1 : 1] + +// __has_nothrow_assign +TRAIT_IS_TRUE(__has_nothrow_assign, __strong id); +TRAIT_IS_TRUE(__has_nothrow_assign, __weak id); +TRAIT_IS_TRUE(__has_nothrow_assign, __autoreleasing id); +TRAIT_IS_TRUE(__has_nothrow_assign, __unsafe_unretained id); + +// __has_nothrow_copy +TRAIT_IS_TRUE(__has_nothrow_copy, __strong id); +TRAIT_IS_TRUE(__has_nothrow_copy, __weak id); +TRAIT_IS_TRUE(__has_nothrow_copy, __autoreleasing id); +TRAIT_IS_TRUE(__has_nothrow_copy, __unsafe_unretained id); + +// __has_nothrow_constructor +TRAIT_IS_TRUE(__has_nothrow_constructor, __strong id); +TRAIT_IS_TRUE(__has_nothrow_constructor, __weak id); +TRAIT_IS_TRUE(__has_nothrow_constructor, __autoreleasing id); +TRAIT_IS_TRUE(__has_nothrow_constructor, __unsafe_unretained id); + +// __has_trivial_assign +TRAIT_IS_FALSE(__has_trivial_assign, __strong id); +TRAIT_IS_FALSE(__has_trivial_assign, __weak id); +TRAIT_IS_FALSE(__has_trivial_assign, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_assign, __unsafe_unretained id); + +// __has_trivial_copy +TRAIT_IS_FALSE(__has_trivial_copy, __strong id); +TRAIT_IS_FALSE(__has_trivial_copy, __weak id); +TRAIT_IS_FALSE(__has_trivial_copy, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_copy, __unsafe_unretained id); + +// __has_trivial_constructor +TRAIT_IS_FALSE(__has_trivial_constructor, __strong id); +TRAIT_IS_FALSE(__has_trivial_constructor, __weak id); +TRAIT_IS_FALSE(__has_trivial_constructor, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_constructor, __unsafe_unretained id); + +// __has_trivial_destructor +TRAIT_IS_FALSE(__has_trivial_destructor, __strong id); +TRAIT_IS_FALSE(__has_trivial_destructor, __weak id); +TRAIT_IS_TRUE(__has_trivial_destructor, __autoreleasing id); +TRAIT_IS_TRUE(__has_trivial_destructor, __unsafe_unretained id); + +// __is_literal +TRAIT_IS_FALSE(__is_literal, __strong id); +TRAIT_IS_FALSE(__is_literal, __weak id); +TRAIT_IS_FALSE(__is_literal, __autoreleasing id); +TRAIT_IS_FALSE(__is_literal, __unsafe_unretained id); + +// __is_literal_type +TRAIT_IS_FALSE(__is_literal_type, __strong id); +TRAIT_IS_FALSE(__is_literal_type, __weak id); +TRAIT_IS_FALSE(__is_literal_type, __autoreleasing id); +TRAIT_IS_FALSE(__is_literal_type, __unsafe_unretained id); + +// __is_pod +TRAIT_IS_FALSE(__is_pod, __strong id); +TRAIT_IS_FALSE(__is_pod, __weak id); +TRAIT_IS_FALSE(__is_pod, __autoreleasing id); +TRAIT_IS_TRUE(__is_pod, __unsafe_unretained id); + +// __is_trivial +TRAIT_IS_FALSE(__is_trivial, __strong id); +TRAIT_IS_FALSE(__is_trivial, __weak id); +TRAIT_IS_FALSE(__is_trivial, __autoreleasing id); +TRAIT_IS_TRUE(__is_trivial, __unsafe_unretained id); + +// __is_scalar +TRAIT_IS_FALSE(__is_scalar, __strong id); +TRAIT_IS_FALSE(__is_scalar, __weak id); +TRAIT_IS_FALSE(__is_scalar, __autoreleasing id); +TRAIT_IS_TRUE(__is_scalar, __unsafe_unretained id); + +// __is_standard_layout +TRAIT_IS_TRUE(__is_standard_layout, __strong id); +TRAIT_IS_TRUE(__is_standard_layout, __weak id); +TRAIT_IS_TRUE(__is_standard_layout, __autoreleasing id); +TRAIT_IS_TRUE(__is_standard_layout, __unsafe_unretained id); + diff --git a/test/SemaObjCXX/arc-unavailable-for-weakref.mm b/test/SemaObjCXX/arc-unavailable-for-weakref.mm new file mode 100644 index 000000000000..a7b357006843 --- /dev/null +++ b/test/SemaObjCXX/arc-unavailable-for-weakref.mm @@ -0,0 +1,47 @@ +// RUN: %clang_cc1 -x objective-c++ -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fobjc-runtime-has-weak -fsyntax-only -fobjc-arc -verify %s +// rdar://9693477 + +__attribute__((objc_arc_weak_reference_unavailable)) +@interface NSOptOut1072 // expected-note {{class is declared here}} +@end + +@interface sub : NSOptOut1072 @end // expected-note 2 {{class is declared here}} + +int main() { + __weak sub *w2; // expected-error {{class is incompatible with __weak references}} + + __weak NSOptOut1072 *ns1; // expected-error {{class is incompatible with __weak references}} + + id obj; + + ns1 = (__weak sub *)obj; // expected-error {{assignment of a weak-unavailable object to a __weak object}} \ + // expected-error {{class is incompatible with __weak references}} +} + +// rdar://9732636 +__attribute__((objc_arc_weak_reference_unavailable)) +@interface NOWEAK ++ (id) new; +@end + +NOWEAK * Test1() { + NOWEAK * strong1 = [NOWEAK new]; + __weak id weak1; + weak1 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + + __weak id weak2 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + return (__weak id)strong1; // expected-error {{cast of weak-unavailable object of type 'NOWEAK *' to a __weak object of type '__weak id'}} +} + +@protocol P @end +@protocol P1 @end + +NOWEAK<P, P1> * Test2() { + NOWEAK<P, P1> * strong1 = 0; + __weak id<P> weak1; + weak1 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + + __weak id<P> weak2 = strong1; // expected-error {{assignment of a weak-unavailable object to a __weak object}} + return (__weak id<P, P1>)strong1; // expected-error {{cast of weak-unavailable object of type 'NOWEAK<P,P1> *' to a __weak object of type '__weak id<P,P1>'}} +} + diff --git a/test/SemaObjCXX/exceptions-fragile.mm b/test/SemaObjCXX/exceptions-fragile.mm index d1e7077089e2..71e259aa6bba 100644 --- a/test/SemaObjCXX/exceptions-fragile.mm +++ b/test/SemaObjCXX/exceptions-fragile.mm @@ -6,7 +6,7 @@ void opaque(); namespace test0 { void test() { try { - } catch (NSException *e) { // expected-error {{can't catch Objective C exceptions in C++ in the non-unified exception model}} + } catch (NSException *e) { // expected-warning {{can not catch an exception thrown with @throw in C++ in the non-unified exception model}} } } } diff --git a/test/SemaObjCXX/gc-attributes.mm b/test/SemaObjCXX/gc-attributes.mm index 70a93b2e33d1..4549683bb2e0 100644 --- a/test/SemaObjCXX/gc-attributes.mm +++ b/test/SemaObjCXX/gc-attributes.mm @@ -3,7 +3,7 @@ @interface A @end -void f0(__strong A**); // expected-note{{candidate function not viable: 1st argument ('A *__weak *') has __weak lifetime, but parameter has __strong lifetime}} +void f0(__strong A**); // expected-note{{candidate function not viable: 1st argument ('A *__weak *') has __weak ownership, but parameter has __strong ownership}} void test_f0() { A *a; @@ -12,7 +12,7 @@ void test_f0() { f0(&a2); // expected-error{{no matching function}} } -void f1(__weak A**); // expected-note{{candidate function not viable: 1st argument ('A *__strong *') has __strong lifetime, but parameter has __weak lifetime}} +void f1(__weak A**); // expected-note{{candidate function not viable: 1st argument ('A *__strong *') has __strong ownership, but parameter has __weak ownership}} void test_f1() { A *a; diff --git a/test/SemaObjCXX/null_objc_pointer.mm b/test/SemaObjCXX/null_objc_pointer.mm new file mode 100644 index 000000000000..0da9e50f5a1c --- /dev/null +++ b/test/SemaObjCXX/null_objc_pointer.mm @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -Wnull-arithmetic %s +#define NULL __null + +@interface X +@end + +void f() { + bool b; + X *d; + b = d < NULL || NULL < d || d > NULL || NULL > d; + b = d <= NULL || NULL <= d || d >= NULL || NULL >= d; + b = d == NULL || NULL == d || d != NULL || NULL != d; +} diff --git a/test/SemaObjCXX/nullptr.mm b/test/SemaObjCXX/nullptr.mm index 4cd5669ef5ae..4a9d1a07de9d 100644 --- a/test/SemaObjCXX/nullptr.mm +++ b/test/SemaObjCXX/nullptr.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++0x -fblocks -fsyntax-only -verify %s @interface A @end @@ -11,3 +11,6 @@ void comparisons(A *a) { void assignment(A *a) { a = nullptr; } + +int PR10145a = (void(^)())0 == nullptr; +int PR10145b = nullptr == (void(^)())0; diff --git a/test/SemaObjCXX/property-type-mismatch.mm b/test/SemaObjCXX/property-type-mismatch.mm new file mode 100644 index 000000000000..059793cf5ceb --- /dev/null +++ b/test/SemaObjCXX/property-type-mismatch.mm @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +// rdar://9740328 + +@protocol P1; + +@interface NSObject +@end + +@interface A : NSObject +@property (assign) NSObject<P1> *prop; +@end + +@protocol P2 <P1> +@end + +@interface B : A +@property (assign) NSObject<P2> *prop; +@end + diff --git a/test/SemaObjCXX/related-result-type-inference.mm b/test/SemaObjCXX/related-result-type-inference.mm index c3cab05194fc..675e6acbda14 100644 --- a/test/SemaObjCXX/related-result-type-inference.mm +++ b/test/SemaObjCXX/related-result-type-inference.mm @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fobjc-infer-related-result-type -verify %s +// RUN: %clang_cc1 -verify %s @interface Unrelated @end diff --git a/test/SemaOpenCL/vector_literals_invalid.cl b/test/SemaOpenCL/vector_literals_invalid.cl new file mode 100644 index 000000000000..957680f44ffe --- /dev/null +++ b/test/SemaOpenCL/vector_literals_invalid.cl @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -verify %s + +typedef __attribute__(( ext_vector_type(4) )) float float4; +typedef __attribute__(( ext_vector_type(4) )) int int4; +typedef __attribute__(( ext_vector_type(8) )) int int8; + +void vector_literals_invalid() +{ + int4 a = (int4)(1,2,3); // expected-error{{too few elements}} + int4 b = (int4)(1,2,3,4,5); // expected-error{{excess elements in vector}} + ((float4)(1.0f))++; // expected-error{{expression is not assignable}} + int8 d = (int8)(a,(float4)(1)); // expected-error{{initializing 'int' with an expression of incompatible type 'float4'}} +} diff --git a/test/SemaTemplate/class-template-decl.cpp b/test/SemaTemplate/class-template-decl.cpp index e7722123f9c2..2e84e93ead97 100644 --- a/test/SemaTemplate/class-template-decl.cpp +++ b/test/SemaTemplate/class-template-decl.cpp @@ -50,7 +50,7 @@ void f() { template<typename T> class X; // expected-error{{expression}} } -template<typename T> class X1 { } var; // expected-error{{declared as a template}} +template<typename T> class X1 var; // expected-error{{declared as a template}} namespace M { } diff --git a/test/SemaTemplate/deduction.cpp b/test/SemaTemplate/deduction.cpp index 15c061c26198..aecb5ee7c8b3 100644 --- a/test/SemaTemplate/deduction.cpp +++ b/test/SemaTemplate/deduction.cpp @@ -150,3 +150,15 @@ namespace test3 { } }; } + +// Verify that we can deduce enum-typed arguments correctly. +namespace test14 { + enum E { E0, E1 }; + template <E> struct A {}; + template <E e> void foo(const A<e> &a) {} + + void test() { + A<E0> a; + foo(a); + } +} diff --git a/test/SemaTemplate/default-arguments.cpp b/test/SemaTemplate/default-arguments.cpp index 9ea0fc2e4d52..6391369aa5c4 100644 --- a/test/SemaTemplate/default-arguments.cpp +++ b/test/SemaTemplate/default-arguments.cpp @@ -121,3 +121,18 @@ X6<long, X5b> x6c; template<template<class> class X = B<int> > struct X7; // expected-error{{must be a class template}} + +namespace PR9643 { + template<typename T> class allocator {}; + template<typename T, typename U = allocator<T> > class vector {}; + + template<template<typename U, typename = allocator<U> > class container, + typename DT> + container<DT> initializer(const DT& d) { + return container<DT>(); + } + + void f() { + vector<int, allocator<int> > v = initializer<vector>(5); + } +} diff --git a/test/SemaTemplate/dependent-names-no-std.cpp b/test/SemaTemplate/dependent-names-no-std.cpp index e9ac99faf65b..2fb9d9909f75 100644 --- a/test/SemaTemplate/dependent-names-no-std.cpp +++ b/test/SemaTemplate/dependent-names-no-std.cpp @@ -11,7 +11,7 @@ namespace PR10053 { template<typename T> struct A { T t; A() { - f(t); // expected-error {{call to function 'f' that is neither visible in the template definition nor found by argument dependent lookup}} + f(t); // expected-error {{call to function 'f' that is neither visible in the template definition nor found by argument-dependent lookup}} } }; diff --git a/test/SemaTemplate/dependent-names.cpp b/test/SemaTemplate/dependent-names.cpp index 2a50df5b7f14..7bab5d18d1ad 100644 --- a/test/SemaTemplate/dependent-names.cpp +++ b/test/SemaTemplate/dependent-names.cpp @@ -148,7 +148,7 @@ namespace PR10053 { template<typename T> struct A { T t; A() { - f(t); // expected-error {{call to function 'f' that is neither visible in the template definition nor found by argument dependent lookup}} + f(t); // expected-error {{call to function 'f' that is neither visible in the template definition nor found by argument-dependent lookup}} } }; @@ -160,7 +160,7 @@ namespace PR10053 { namespace N { namespace M { template<typename T> int g(T t) { - f(t); // expected-error {{call to function 'f' that is neither visible in the template definition nor found by argument dependent lookup}} + f(t); // expected-error {{call to function 'f' that is neither visible in the template definition nor found by argument-dependent lookup}} }; } @@ -187,7 +187,7 @@ namespace PR10053 { // Example from www/compatibility.html namespace my_file { template <typename T> T Squared(T x) { - return Multiply(x, x); // expected-error {{neither visible in the template definition nor found by argument dependent lookup}} + return Multiply(x, x); // expected-error {{neither visible in the template definition nor found by argument-dependent lookup}} } int Multiply(int x, int y) { // expected-note {{should be declared prior to the call site}} @@ -203,7 +203,7 @@ namespace PR10053 { namespace my_file2 { template<typename T> void Dump(const T& value) { - std::cout << value << "\n"; // expected-error {{neither visible in the template definition nor found by argument dependent lookup}} + std::cout << value << "\n"; // expected-error {{neither visible in the template definition nor found by argument-dependent lookup}} } namespace ns { @@ -222,7 +222,7 @@ namespace PR10053 { namespace my_file2_a { template<typename T> void Dump(const T &value) { - print(std::cout, value); // expected-error 4{{neither visible in the template definition nor found by argument dependent lookup}} + print(std::cout, value); // expected-error 4{{neither visible in the template definition nor found by argument-dependent lookup}} } namespace ns { @@ -248,7 +248,7 @@ namespace PR10053 { namespace unary { template<typename T> T Negate(const T& value) { - return !value; // expected-error {{call to function 'operator!' that is neither visible in the template definition nor found by argument dependent lookup}} + return !value; // expected-error {{call to function 'operator!' that is neither visible in the template definition nor found by argument-dependent lookup}} } namespace ns { @@ -262,3 +262,33 @@ namespace PR10053 { } } } + +namespace PR10187 { + namespace A { + template<typename T> + struct S { + void f() { + for (auto &a : e) + __range(a); // expected-error {{undeclared identifier '__range'}} + } + int e[10]; + }; + void g() { + S<int>().f(); // expected-note {{here}} + } + } + + namespace B { + template<typename T> void g(); // expected-note {{not viable}} + template<typename T> void f() { + g<int>(T()); // expected-error {{no matching function}} + } + + namespace { + struct S {}; + } + void g(S); + + template void f<S>(); // expected-note {{here}} + } +} diff --git a/test/SemaTemplate/ext-vector-type.cpp b/test/SemaTemplate/ext-vector-type.cpp index 7334e88e9267..f968c13f1023 100644 --- a/test/SemaTemplate/ext-vector-type.cpp +++ b/test/SemaTemplate/ext-vector-type.cpp @@ -58,3 +58,37 @@ int test_make6() { y.x = -1; y.w = -1; // expected-error{{vector component access exceeds type}} } + +namespace Deduction { + template<typename T> struct X0; + + template<typename T, unsigned N> + struct X0<T __attribute__((ext_vector_type(N)))> { + static const unsigned value = 0; + }; + + template<typename T> + struct X0<T __attribute__((ext_vector_type(4)))> { + static const unsigned value = 1; + }; + + template<unsigned N> + struct X0<float __attribute__((ext_vector_type(N)))> { + static const unsigned value = 2; + }; + + template<> + struct X0<float __attribute__((ext_vector_type(4)))> { + static const unsigned value = 3; + }; + + typedef int __attribute__((ext_vector_type(2))) int2; + typedef int __attribute__((ext_vector_type(4))) int4; + typedef float __attribute__((ext_vector_type(2))) float2; + typedef float __attribute__((ext_vector_type(4))) float4; + + int array0[X0<int2>::value == 0? 1 : -1]; + int array1[X0<int4>::value == 1? 1 : -1]; + int array2[X0<float2>::value == 2? 1 : -1]; + int array3[X0<float4>::value == 3? 1 : -1]; +} diff --git a/test/SemaTemplate/instantiate-call.cpp b/test/SemaTemplate/instantiate-call.cpp index a0e48c8dec77..da1eb49eb89e 100644 --- a/test/SemaTemplate/instantiate-call.cpp +++ b/test/SemaTemplate/instantiate-call.cpp @@ -25,7 +25,7 @@ namespace N3 { struct call_f0 { void test_f0(T t) { Result &result = f0(t); // expected-error {{undeclared identifier}} \ - expected-error {{neither visible in the template definition nor found by argument dependent lookup}} + expected-error {{neither visible in the template definition nor found by argument-dependent lookup}} } }; } diff --git a/test/SemaTemplate/instantiate-function-2.cpp b/test/SemaTemplate/instantiate-function-2.cpp index ebc0ef3a9f96..087ede2b89cb 100644 --- a/test/SemaTemplate/instantiate-function-2.cpp +++ b/test/SemaTemplate/instantiate-function-2.cpp @@ -31,3 +31,36 @@ namespace UsedAttr { foo<int>(); // expected-note{{instantiation of}} } } + +namespace PR9654 { + typedef void ftype(int); + + template<typename T> + ftype f; + + void g() { + f<int>(0); + } +} + +namespace AliasTagDef { + template<typename T> + T f() { + using S = struct { // expected-warning {{C++0x}} + T g() { + return T(); + } + }; + return S().g(); + } + + int n = f<int>(); +} + +namespace PR10273 { + template<typename T> void (f)(T t) {} + + void g() { + (f)(17); + } +} diff --git a/test/SemaTemplate/instantiate-member-class.cpp b/test/SemaTemplate/instantiate-member-class.cpp index 74c2609dcd0f..1028b45cc0e1 100644 --- a/test/SemaTemplate/instantiate-member-class.cpp +++ b/test/SemaTemplate/instantiate-member-class.cpp @@ -104,3 +104,17 @@ namespace test2 { }; template class C<int>; } + +namespace AliasTagDef { + template<typename T> + struct F { + using S = struct U { // expected-warning {{C++0x}} + T g() { + return T(); + } + }; + }; + + int m = F<int>::S().g(); + int n = F<int>::U().g(); +} diff --git a/test/SemaTemplate/instantiate-try-catch.cpp b/test/SemaTemplate/instantiate-try-catch.cpp index 1c6c26f2586e..f4ce0e173146 100644 --- a/test/SemaTemplate/instantiate-try-catch.cpp +++ b/test/SemaTemplate/instantiate-try-catch.cpp @@ -12,3 +12,20 @@ template struct TryCatch0<int&>; // okay template struct TryCatch0<int&&>; // expected-note{{instantiation}} template struct TryCatch0<int>; // expected-note{{instantiation}} + +namespace PR10232 { + template <typename T> + class Templated { + struct Exception { + private: + Exception(const Exception&); // expected-note{{declared private here}} + }; + void exception() { + try { + } catch(Exception e) { // expected-error{{calling a private constructor of class 'PR10232::Templated<int>::Exception'}} + } + } + }; + + template class Templated<int>; // expected-note{{in instantiation of member function 'PR10232::Templated<int>::exception' requested here}} +} diff --git a/test/SemaTemplate/member-inclass-init-value-dependent.cpp b/test/SemaTemplate/member-inclass-init-value-dependent.cpp new file mode 100644 index 000000000000..d1ae4f2ded5f --- /dev/null +++ b/test/SemaTemplate/member-inclass-init-value-dependent.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_cc1 -emit-llvm-only %s +// PR10290 + +template<int Flags> struct foo { + int value = Flags && 0; +}; + +void test() { + foo<4> bar; +} + diff --git a/test/SemaTemplate/unresolved-construct.cpp b/test/SemaTemplate/unresolved-construct.cpp new file mode 100644 index 000000000000..0d1ba17a0179 --- /dev/null +++ b/test/SemaTemplate/unresolved-construct.cpp @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -std=c++0x -fsyntax-only -verify %s +class A +{ +public: + A() {} + + template <class _F> + explicit A(_F&& __f); + + A(A&&) {} + A& operator=(A&&) {return *this;} +}; + +template <class T> +void f(T t) +{ + A a; + a = f(t); +} diff --git a/test/Unit/lit.cfg b/test/Unit/lit.cfg index 794234ce5575..8f27781523f0 100644 --- a/test/Unit/lit.cfg +++ b/test/Unit/lit.cfg @@ -30,14 +30,6 @@ if 'TEMP' in os.environ: ### -# If necessary, point the dynamic loader at libLLVM.so. -if config.enable_shared: - shlibpath = config.environment.get(config.shlibpath_var,'') - if shlibpath: - shlibpath = os.pathsep + shlibpath - shlibpath = config.shlibdir + shlibpath - config.environment[config.shlibpath_var] = shlibpath - # Check that the object root is known. if config.test_exec_root is None: # Otherwise, we haven't loaded the site specific configuration (the user is @@ -71,7 +63,7 @@ if config.test_exec_root is None: # Validate that we got a tree which points to here, using the standard # tools/clang layout. - this_src_root = os.path.dirname(config.test_source_root) + this_src_root = os.path.join(os.path.dirname(__file__),'..','..') if os.path.realpath(clang_src_root) != os.path.realpath(this_src_root): lit.fatal('No site specific configuration available!') @@ -84,3 +76,11 @@ if config.test_exec_root is None: lit.note('using out-of-tree build at %r' % clang_obj_root) lit.load_config(config, site_cfg) raise SystemExit + +# If necessary, point the dynamic loader at libLLVM.so. +if config.enable_shared: + shlibpath = config.environment.get(config.shlibpath_var,'') + if shlibpath: + shlibpath = os.pathsep + shlibpath + shlibpath = config.shlibdir + shlibpath + config.environment[config.shlibpath_var] = shlibpath diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index ae33b782d49d..aff437fd467a 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,3 +1,5 @@ add_subdirectory(libclang) add_subdirectory(c-index-test) +add_subdirectory(arcmt-test) +add_subdirectory(c-arcmt-test) add_subdirectory(driver) diff --git a/tools/Makefile b/tools/Makefile index 000293f09c9f..bfd2a641ec1e 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -8,12 +8,12 @@ ##===----------------------------------------------------------------------===## CLANG_LEVEL := .. -DIRS := driver libclang c-index-test +DIRS := driver libclang c-index-test arcmt-test c-arcmt-test include $(CLANG_LEVEL)/../../Makefile.config ifeq ($(OS), $(filter $(OS), Minix)) -DIRS := $(filter-out libclang c-index-test, $(DIRS)) +DIRS := $(filter-out libclang c-index-test, arcmt-test, $(DIRS)) endif include $(CLANG_LEVEL)/Makefile diff --git a/tools/arcmt-test/CMakeLists.txt b/tools/arcmt-test/CMakeLists.txt new file mode 100644 index 000000000000..9227f8ee63c7 --- /dev/null +++ b/tools/arcmt-test/CMakeLists.txt @@ -0,0 +1,13 @@ +set(LLVM_USED_LIBS + clangARCMigrate + clangRewrite + ) + +set( LLVM_LINK_COMPONENTS + support + mc + ) + +add_clang_executable(arcmt-test + arcmt-test.cpp + ) diff --git a/tools/arcmt-test/Makefile b/tools/arcmt-test/Makefile new file mode 100644 index 000000000000..c143e27f33e2 --- /dev/null +++ b/tools/arcmt-test/Makefile @@ -0,0 +1,24 @@ +##===- tools/arcmt-test/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = arcmt-test + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# Don't install this. It is used for tests. +NO_INSTALL = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clangIndex.a clangARCMigrate.a clangRewrite.a \ + clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ + clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile diff --git a/tools/arcmt-test/arcmt-test.cpp b/tools/arcmt-test/arcmt-test.cpp new file mode 100644 index 000000000000..eb0f56943f96 --- /dev/null +++ b/tools/arcmt-test/arcmt-test.cpp @@ -0,0 +1,374 @@ +//===-- arcmt-test.cpp - ARC Migration Tool testbed -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/TextDiagnosticPrinter.h" +#include "clang/Frontend/VerifyDiagnosticsClient.h" +#include "clang/Frontend/Utils.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/system_error.h" + +using namespace clang; +using namespace arcmt; + +static llvm::cl::opt<bool> +CheckOnly("check-only", + llvm::cl::desc("Just check for issues that need to be handled manually")); + +//static llvm::cl::opt<bool> +//TestResultForARC("test-result", +//llvm::cl::desc("Test the result of transformations by parsing it in ARC mode")); + +static llvm::cl::opt<bool> +OutputTransformations("output-transformations", + llvm::cl::desc("Print the source transformations")); + +static llvm::cl::opt<bool> +VerifyDiags("verify",llvm::cl::desc("Verify emitted diagnostics and warnings")); + +static llvm::cl::opt<bool> +VerboseOpt("v", llvm::cl::desc("Enable verbose output")); + +static llvm::cl::opt<bool> +VerifyTransformedFiles("verify-transformed-files", +llvm::cl::desc("Read pairs of file mappings (typically the output of " + "c-arcmt-test) and compare their contents with the filenames " + "provided in command-line")); + +static llvm::cl::opt<std::string> +RemappingsFile("remappings-file", + llvm::cl::desc("Pairs of file mappings (typically the output of " + "c-arcmt-test)")); + +static llvm::cl::list<std::string> +ResultFiles(llvm::cl::Positional, llvm::cl::desc("<filename>...")); + +static llvm::cl::extrahelp extraHelp( + "\nusage with compiler args: arcmt-test [options] --args [compiler flags]\n"); + +// This function isn't referenced outside its translation unit, but it +// can't use the "static" keyword because its address is used for +// GetMainExecutable (since some platforms don't support taking the +// address of main, and some platforms can't implement GetMainExecutable +// without being given the address of a function in the main executable). +llvm::sys::Path GetExecutablePath(const char *Argv0) { + // This just needs to be some symbol in the binary; C++ doesn't + // allow taking the address of ::main however. + void *MainAddr = (void*) (intptr_t) GetExecutablePath; + return llvm::sys::Path::GetMainExecutable(Argv0, MainAddr); +} + +static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, + llvm::raw_ostream &OS); +static void printSourceRange(CharSourceRange range, ASTContext &Ctx, + llvm::raw_ostream &OS); + +namespace { + +class PrintTransforms : public MigrationProcess::RewriteListener { + ASTContext *Ctx; + llvm::raw_ostream &OS; + +public: + PrintTransforms(llvm::raw_ostream &OS) + : Ctx(0), OS(OS) { } + + virtual void start(ASTContext &ctx) { Ctx = &ctx; } + virtual void finish() { Ctx = 0; } + + virtual void insert(SourceLocation loc, llvm::StringRef text) { + assert(Ctx); + OS << "Insert: "; + printSourceLocation(loc, *Ctx, OS); + OS << " \"" << text << "\"\n"; + } + + virtual void remove(CharSourceRange range) { + assert(Ctx); + OS << "Remove: "; + printSourceRange(range, *Ctx, OS); + OS << '\n'; + } +}; + +} // anonymous namespace + +static bool checkForMigration(llvm::StringRef resourcesPath, + llvm::ArrayRef<const char *> Args) { + DiagnosticClient *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions()); + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> Diags(new Diagnostic(DiagID, DiagClient)); + // Chain in -verify checker, if requested. + VerifyDiagnosticsClient *verifyDiag = 0; + if (VerifyDiags) { + verifyDiag = new VerifyDiagnosticsClient(*Diags, Diags->takeClient()); + Diags->setClient(verifyDiag); + } + + CompilerInvocation CI; + CompilerInvocation::CreateFromArgs(CI, Args.begin(), Args.end(), *Diags); + + if (CI.getFrontendOpts().Inputs.empty()) { + llvm::errs() << "error: no input files\n"; + return true; + } + + if (!CI.getLangOpts().ObjC1) + return false; + + arcmt::checkForManualIssues(CI, + CI.getFrontendOpts().Inputs[0].second, + CI.getFrontendOpts().Inputs[0].first, + Diags->getClient()); + return Diags->getClient()->getNumErrors() > 0; +} + +static void printResult(FileRemapper &remapper, llvm::raw_ostream &OS) { + CompilerInvocation CI; + remapper.applyMappings(CI); + PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); + // The changed files will be in memory buffers, print them. + for (unsigned i = 0, e = PPOpts.RemappedFileBuffers.size(); i != e; ++i) { + const llvm::MemoryBuffer *mem = PPOpts.RemappedFileBuffers[i].second; + OS << mem->getBuffer(); + } +} + +static bool performTransformations(llvm::StringRef resourcesPath, + llvm::ArrayRef<const char *> Args) { + // Check first. + if (checkForMigration(resourcesPath, Args)) + return true; + + DiagnosticClient *DiagClient = + new TextDiagnosticPrinter(llvm::errs(), DiagnosticOptions()); + llvm::IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + llvm::IntrusiveRefCntPtr<Diagnostic> TopDiags(new Diagnostic(DiagID, DiagClient)); + + CompilerInvocation origCI; + CompilerInvocation::CreateFromArgs(origCI, Args.begin(), Args.end(), + *TopDiags); + + if (origCI.getFrontendOpts().Inputs.empty()) { + llvm::errs() << "error: no input files\n"; + return true; + } + + if (!origCI.getLangOpts().ObjC1) + return false; + + MigrationProcess migration(origCI, DiagClient); + + std::vector<TransformFn> transforms = arcmt::getAllTransformations(); + assert(!transforms.empty()); + + llvm::OwningPtr<PrintTransforms> transformPrinter; + if (OutputTransformations) + transformPrinter.reset(new PrintTransforms(llvm::outs())); + + for (unsigned i=0, e = transforms.size(); i != e; ++i) { + bool err = migration.applyTransform(transforms[i], transformPrinter.get()); + if (err) return true; + + if (VerboseOpt) { + if (i == e-1) + llvm::errs() << "\n##### FINAL RESULT #####\n"; + else + llvm::errs() << "\n##### OUTPUT AFTER "<< i+1 <<". TRANSFORMATION #####\n"; + printResult(migration.getRemapper(), llvm::errs()); + llvm::errs() << "\n##########################\n\n"; + } + } + + if (!OutputTransformations) + printResult(migration.getRemapper(), llvm::outs()); + + // FIXME: TestResultForARC + + return false; +} + +static bool filesCompareEqual(llvm::StringRef fname1, llvm::StringRef fname2) { + using namespace llvm; + + OwningPtr<MemoryBuffer> file1; + MemoryBuffer::getFile(fname1, file1); + if (!file1) + return false; + + OwningPtr<MemoryBuffer> file2; + MemoryBuffer::getFile(fname2, file2); + if (!file2) + return false; + + return file1->getBuffer() == file2->getBuffer(); +} + +static bool verifyTransformedFiles(llvm::ArrayRef<std::string> resultFiles) { + using namespace llvm; + + assert(!resultFiles.empty()); + + std::map<StringRef, StringRef> resultMap; + + for (ArrayRef<std::string>::iterator + I = resultFiles.begin(), E = resultFiles.end(); I != E; ++I) { + StringRef fname(*I); + if (!fname.endswith(".result")) { + errs() << "error: filename '" << fname + << "' does not have '.result' extension\n"; + return true; + } + resultMap[sys::path::stem(fname)] = fname; + } + + OwningPtr<MemoryBuffer> inputBuf; + if (RemappingsFile.empty()) + MemoryBuffer::getSTDIN(inputBuf); + else + MemoryBuffer::getFile(RemappingsFile, inputBuf); + if (!inputBuf) { + errs() << "error: could not read remappings input\n"; + return true; + } + + SmallVector<StringRef, 8> strs; + inputBuf->getBuffer().split(strs, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false); + + if (strs.empty()) { + errs() << "error: no files to verify from stdin\n"; + return true; + } + if (strs.size() % 2 != 0) { + errs() << "error: files to verify are not original/result pairs\n"; + return true; + } + + for (unsigned i = 0, e = strs.size(); i != e; i += 2) { + StringRef inputOrigFname = strs[i]; + StringRef inputResultFname = strs[i+1]; + + std::map<StringRef, StringRef>::iterator It; + It = resultMap.find(sys::path::filename(inputOrigFname)); + if (It == resultMap.end()) { + errs() << "error: '" << inputOrigFname << "' is not in the list of " + << "transformed files to verify\n"; + return true; + } + + bool exists = false; + sys::fs::exists(It->second, exists); + if (!exists) { + errs() << "error: '" << It->second << "' does not exist\n"; + return true; + } + sys::fs::exists(inputResultFname, exists); + if (!exists) { + errs() << "error: '" << inputResultFname << "' does not exist\n"; + return true; + } + + if (!filesCompareEqual(It->second, inputResultFname)) { + errs() << "error: '" << It->second << "' is different than " + << "'" << inputResultFname << "'\n"; + return true; + } + + resultMap.erase(It); + } + + if (!resultMap.empty()) { + for (std::map<StringRef, StringRef>::iterator + I = resultMap.begin(), E = resultMap.end(); I != E; ++I) + errs() << "error: '" << I->second << "' was not verified!\n"; + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// Misc. functions. +//===----------------------------------------------------------------------===// + +static void printSourceLocation(SourceLocation loc, ASTContext &Ctx, + llvm::raw_ostream &OS) { + SourceManager &SM = Ctx.getSourceManager(); + PresumedLoc PL = SM.getPresumedLoc(loc); + + OS << llvm::sys::path::filename(PL.getFilename()); + OS << ":" << PL.getLine() << ":" + << PL.getColumn(); +} + +static void printSourceRange(CharSourceRange range, ASTContext &Ctx, + llvm::raw_ostream &OS) { + SourceManager &SM = Ctx.getSourceManager(); + const LangOptions &langOpts = Ctx.getLangOptions(); + + PresumedLoc PL = SM.getPresumedLoc(range.getBegin()); + + OS << llvm::sys::path::filename(PL.getFilename()); + OS << " [" << PL.getLine() << ":" + << PL.getColumn(); + OS << " - "; + + SourceLocation end = range.getEnd(); + PL = SM.getPresumedLoc(end); + + unsigned endCol = PL.getColumn() - 1; + if (!range.isTokenRange()) + endCol += Lexer::MeasureTokenLength(end, SM, langOpts); + OS << PL.getLine() << ":" << endCol << "]"; +} + +//===----------------------------------------------------------------------===// +// Command line processing. +//===----------------------------------------------------------------------===// + +int main(int argc, const char **argv) { + using llvm::StringRef; + void *MainAddr = (void*) (intptr_t) GetExecutablePath; + llvm::sys::PrintStackTraceOnErrorSignal(); + + std::string + resourcesPath = CompilerInvocation::GetResourcesPath(argv[0], MainAddr); + + int optargc = 0; + for (; optargc != argc; ++optargc) { + if (StringRef(argv[optargc]) == "--args") + break; + } + llvm::cl::ParseCommandLineOptions(optargc, const_cast<char **>(argv), "arcmt-test"); + + if (VerifyTransformedFiles) { + if (ResultFiles.empty()) { + llvm::cl::PrintHelpMessage(); + return 1; + } + return verifyTransformedFiles(ResultFiles); + } + + if (optargc == argc) { + llvm::cl::PrintHelpMessage(); + return 1; + } + + llvm::ArrayRef<const char*> Args(argv+optargc+1, argc-optargc-1); + + if (CheckOnly) + return checkForMigration(resourcesPath, Args); + + return performTransformations(resourcesPath, Args); +} diff --git a/tools/c-arcmt-test/CMakeLists.txt b/tools/c-arcmt-test/CMakeLists.txt new file mode 100644 index 000000000000..351f4adca10f --- /dev/null +++ b/tools/c-arcmt-test/CMakeLists.txt @@ -0,0 +1,14 @@ +set(LLVM_USED_LIBS libclang) + +set( LLVM_LINK_COMPONENTS + support + mc + ) + +add_clang_executable(c-arcmt-test + c-arcmt-test.c + ) + +set_target_properties(c-arcmt-test + PROPERTIES + LINKER_LANGUAGE CXX) diff --git a/tools/c-arcmt-test/Makefile b/tools/c-arcmt-test/Makefile new file mode 100644 index 000000000000..b72a08ab53bb --- /dev/null +++ b/tools/c-arcmt-test/Makefile @@ -0,0 +1,25 @@ +##===- tools/c-arcmt-test/Makefile -------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +CLANG_LEVEL := ../.. + +TOOLNAME = c-arcmt-test + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +# Don't install this. It is used for tests. +NO_INSTALL = 1 + +LINK_COMPONENTS := support mc +USEDLIBS = clang.a clangIndex.a clangARCMigrate.a clangRewrite.a \ + clangFrontend.a clangDriver.a \ + clangSerialization.a clangParse.a clangSema.a \ + clangAnalysis.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile diff --git a/tools/c-arcmt-test/c-arcmt-test.c b/tools/c-arcmt-test/c-arcmt-test.c new file mode 100644 index 000000000000..5522b33e23a5 --- /dev/null +++ b/tools/c-arcmt-test/c-arcmt-test.c @@ -0,0 +1,91 @@ +/* c-arcmt-test.c */ + +#include "clang-c/Index.h" +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#if defined(_WIN32) +#include <io.h> +#include <fcntl.h> +#endif + +static int print_remappings(const char *path) { + CXRemapping remap; + unsigned i, N; + CXString origFname; + CXString transFname; + + remap = clang_getRemappings(path); + if (!remap) + return 1; + + N = clang_remap_getNumFiles(remap); + for (i = 0; i != N; ++i) { + clang_remap_getFilenames(remap, i, &origFname, &transFname); + + fprintf(stdout, "%s\n", clang_getCString(origFname)); + fprintf(stdout, "%s\n", clang_getCString(transFname)); + + clang_disposeString(origFname); + clang_disposeString(transFname); + } + + clang_remap_dispose(remap); + return 0; +} + +/******************************************************************************/ +/* Command line processing. */ +/******************************************************************************/ + +static void print_usage(void) { + fprintf(stderr, + "usage: c-arcmt-test -arcmt-migrate-directory <path>\n\n\n"); +} + +/***/ + +int carcmttest_main(int argc, const char **argv) { + clang_enableStackTraces(); + if (argc == 3 && strncmp(argv[1], "-arcmt-migrate-directory", 24) == 0) + return print_remappings(argv[2]); + + print_usage(); + return 1; +} + +/***/ + +/* We intentionally run in a separate thread to ensure we at least minimal + * testing of a multithreaded environment (for example, having a reduced stack + * size). */ + +typedef struct thread_info { + int argc; + const char **argv; + int result; +} thread_info; +void thread_runner(void *client_data_v) { + thread_info *client_data = client_data_v; + client_data->result = carcmttest_main(client_data->argc, client_data->argv); +} + +int main(int argc, const char **argv) { + thread_info client_data; + +#if defined(_WIN32) + if (getenv("LIBCLANG_LOGGING") == NULL) + putenv("LIBCLANG_LOGGING=1"); + _setmode( _fileno(stdout), _O_BINARY ); +#else + setenv("LIBCLANG_LOGGING", "1", /*overwrite=*/0); +#endif + + if (getenv("CINDEXTEST_NOTHREADS")) + return carcmttest_main(argc, argv); + + client_data.argc = argc; + client_data.argv = argv; + clang_executeOnThread(thread_runner, &client_data, 0); + return client_data.result; +} diff --git a/tools/c-index-test/Makefile b/tools/c-index-test/Makefile index 68064041f435..5e5b8570c812 100644 --- a/tools/c-index-test/Makefile +++ b/tools/c-index-test/Makefile @@ -18,7 +18,7 @@ NO_INSTALL = 1 LINK_COMPONENTS := support mc USEDLIBS = clang.a clangIndex.a clangFrontend.a clangDriver.a \ - clangSerialization.a clangParse.a clangSema.a clangAnalysis.a \ - clangAST.a clangLex.a clangBasic.a + clangSerialization.a clangParse.a clangSema.a \ + clangAnalysis.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/c-index-test/c-index-test.c b/tools/c-index-test/c-index-test.c index 837fc8929e52..6e0aaac73cc3 100644 --- a/tools/c-index-test/c-index-test.c +++ b/tools/c-index-test/c-index-test.c @@ -38,7 +38,7 @@ static unsigned getDefaultParsingOptions() { if (getenv("CINDEXTEST_COMPLETION_CACHING")) options |= CXTranslationUnit_CacheCompletionResults; if (getenv("CINDEXTEST_NESTED_MACROS")) - options |= CXTranslationUnit_NestedMacroInstantiations; + options |= CXTranslationUnit_NestedMacroExpansions; return options; } @@ -1018,6 +1018,79 @@ void print_completion_result(CXCompletionResult *completion_result, fprintf(file, "\n"); } +void print_completion_contexts(unsigned long long contexts, FILE *file) { + fprintf(file, "Completion contexts:\n"); + if (contexts == CXCompletionContext_Unknown) { + fprintf(file, "Unknown\n"); + } + if (contexts & CXCompletionContext_AnyType) { + fprintf(file, "Any type\n"); + } + if (contexts & CXCompletionContext_AnyValue) { + fprintf(file, "Any value\n"); + } + if (contexts & CXCompletionContext_ObjCObjectValue) { + fprintf(file, "Objective-C object value\n"); + } + if (contexts & CXCompletionContext_ObjCSelectorValue) { + fprintf(file, "Objective-C selector value\n"); + } + if (contexts & CXCompletionContext_CXXClassTypeValue) { + fprintf(file, "C++ class type value\n"); + } + if (contexts & CXCompletionContext_DotMemberAccess) { + fprintf(file, "Dot member access\n"); + } + if (contexts & CXCompletionContext_ArrowMemberAccess) { + fprintf(file, "Arrow member access\n"); + } + if (contexts & CXCompletionContext_ObjCPropertyAccess) { + fprintf(file, "Objective-C property access\n"); + } + if (contexts & CXCompletionContext_EnumTag) { + fprintf(file, "Enum tag\n"); + } + if (contexts & CXCompletionContext_UnionTag) { + fprintf(file, "Union tag\n"); + } + if (contexts & CXCompletionContext_StructTag) { + fprintf(file, "Struct tag\n"); + } + if (contexts & CXCompletionContext_ClassTag) { + fprintf(file, "Class name\n"); + } + if (contexts & CXCompletionContext_Namespace) { + fprintf(file, "Namespace or namespace alias\n"); + } + if (contexts & CXCompletionContext_NestedNameSpecifier) { + fprintf(file, "Nested name specifier\n"); + } + if (contexts & CXCompletionContext_ObjCInterface) { + fprintf(file, "Objective-C interface\n"); + } + if (contexts & CXCompletionContext_ObjCProtocol) { + fprintf(file, "Objective-C protocol\n"); + } + if (contexts & CXCompletionContext_ObjCCategory) { + fprintf(file, "Objective-C category\n"); + } + if (contexts & CXCompletionContext_ObjCInstanceMessage) { + fprintf(file, "Objective-C instance method\n"); + } + if (contexts & CXCompletionContext_ObjCClassMessage) { + fprintf(file, "Objective-C class method\n"); + } + if (contexts & CXCompletionContext_ObjCSelectorName) { + fprintf(file, "Objective-C selector name\n"); + } + if (contexts & CXCompletionContext_MacroName) { + fprintf(file, "Macro name\n"); + } + if (contexts & CXCompletionContext_NaturalLanguage) { + fprintf(file, "Natural language\n"); + } +} + int my_stricmp(const char *s1, const char *s2) { while (*s1 && *s2) { int c1 = tolower((unsigned char)*s1), c2 = tolower((unsigned char)*s2); @@ -1099,6 +1172,7 @@ int perform_code_completion(int argc, const char **argv, int timing_only) { if (results) { unsigned i, n = results->NumResults; + unsigned long long contexts; if (!timing_only) { /* Sort the code-completion results based on the typed text. */ clang_sortCodeCompletionResults(results->Results, results->NumResults); @@ -1112,6 +1186,10 @@ int perform_code_completion(int argc, const char **argv, int timing_only) { PrintDiagnostic(diag); clang_disposeDiagnostic(diag); } + + contexts = clang_codeCompleteGetContexts(results); + print_completion_contexts(contexts, stdout); + clang_disposeCodeCompleteResults(results); } clang_disposeTranslationUnit(TU); @@ -1496,6 +1574,7 @@ int write_pch_file(const char *filename, int argc, const char *argv[]) { CXTranslationUnit TU; struct CXUnsavedFile *unsaved_files = 0; int num_unsaved_files = 0; + int result = 0; Idx = clang_createIndex(/* excludeDeclsFromPCH */1, /* displayDiagnosics=*/1); @@ -1517,12 +1596,34 @@ int write_pch_file(const char *filename, int argc, const char *argv[]) { return 1; } - if (clang_saveTranslationUnit(TU, filename, clang_defaultSaveOptions(TU))) - fprintf(stderr, "Unable to write PCH file %s\n", filename); + switch (clang_saveTranslationUnit(TU, filename, + clang_defaultSaveOptions(TU))) { + case CXSaveError_None: + break; + + case CXSaveError_TranslationErrors: + fprintf(stderr, "Unable to write PCH file %s: translation errors\n", + filename); + result = 2; + break; + + case CXSaveError_InvalidTU: + fprintf(stderr, "Unable to write PCH file %s: invalid translation unit\n", + filename); + result = 3; + break; + + case CXSaveError_Unknown: + default: + fprintf(stderr, "Unable to write PCH file %s: unknown error \n", filename); + result = 1; + break; + } + clang_disposeTranslationUnit(TU); free_remapped_files(unsaved_files, num_unsaved_files); clang_disposeIndex(Idx); - return 0; + return result; } /******************************************************************************/ diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 0c41490175e9..e6d0f1ac3763 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -9,6 +9,7 @@ set( LLVM_USED_LIBS clangIndex clangLex clangParse + clangARCMigrate clangRewrite clangSema clangSerialization diff --git a/tools/driver/Makefile b/tools/driver/Makefile index abe70983df42..1ba8bc24d1fb 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -41,7 +41,7 @@ USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ clangStaticAnalyzerFrontend.a clangStaticAnalyzerCheckers.a \ clangStaticAnalyzerCore.a \ - clangAnalysis.a clangIndex.a clangRewrite.a \ + clangAnalysis.a clangIndex.a clangARCMigrate.a clangRewrite.a \ clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/driver/cc1_main.cpp b/tools/driver/cc1_main.cpp index 535eaa9c96f8..9ad4af1436ca 100644 --- a/tools/driver/cc1_main.cpp +++ b/tools/driver/cc1_main.cpp @@ -126,6 +126,8 @@ int cc1_main(const char **ArgBegin, const char **ArgEnd, // Initialize targets first, so that --version shows registered targets. llvm::InitializeAllTargets(); + llvm::InitializeAllMCAsmInfos(); + llvm::InitializeAllMCSubtargetInfos(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); diff --git a/tools/driver/cc1as_main.cpp b/tools/driver/cc1as_main.cpp index f449235db4c4..358d746986c8 100644 --- a/tools/driver/cc1as_main.cpp +++ b/tools/driver/cc1as_main.cpp @@ -29,7 +29,9 @@ #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCContext.h" +#include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/ErrorHandling.h" @@ -47,11 +49,13 @@ #include "llvm/Target/TargetAsmInfo.h" #include "llvm/Target/TargetAsmParser.h" #include "llvm/Target/TargetData.h" +#include "llvm/Target/TargetInstrInfo.h" #include "llvm/Target/TargetLowering.h" #include "llvm/Target/TargetLoweringObjectFile.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetRegistry.h" #include "llvm/Target/TargetSelect.h" +#include "llvm/Target/TargetSubtargetInfo.h" using namespace clang; using namespace clang::driver; using namespace llvm; @@ -252,7 +256,7 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) { // it later. SrcMgr.setIncludeDirs(Opts.IncludePaths); - OwningPtr<MCAsmInfo> MAI(TheTarget->createAsmInfo(Opts.Triple)); + OwningPtr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(Opts.Triple)); assert(MAI && "Unable to create target asm info!"); bool IsBinary = Opts.OutputType == AssemblerInvocation::FT_Obj; @@ -261,7 +265,8 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) { return false; // FIXME: We shouldn't need to do this (and link in codegen). - OwningPtr<TargetMachine> TM(TheTarget->createTargetMachine(Opts.Triple, "")); + OwningPtr<TargetMachine> TM(TheTarget->createTargetMachine(Opts.Triple, + "", "")); if (!TM) { Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; return false; @@ -278,14 +283,16 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) { TM->getTargetLowering()->getObjFileLowering(); const_cast<TargetLoweringObjectFile&>(TLOF).Initialize(Ctx, *TM); + const MCSubtargetInfo &STI = TM->getSubtarget<MCSubtargetInfo>(); + // FIXME: There is a bit of code duplication with addPassesToEmitFile. if (Opts.OutputType == AssemblerInvocation::FT_Asm) { MCInstPrinter *IP = - TheTarget->createMCInstPrinter(*TM, Opts.OutputAsmVariant, *MAI); + TheTarget->createMCInstPrinter(Opts.OutputAsmVariant, *MAI); MCCodeEmitter *CE = 0; TargetAsmBackend *TAB = 0; if (Opts.ShowEncoding) { - CE = TheTarget->createCodeEmitter(*TM, Ctx); + CE = TheTarget->createCodeEmitter(*TM->getInstrInfo(), STI, Ctx); TAB = TheTarget->createAsmBackend(Opts.Triple); } Str.reset(TheTarget->createAsmStreamer(Ctx, *Out, /*asmverbose*/true, @@ -297,7 +304,8 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) { } else { assert(Opts.OutputType == AssemblerInvocation::FT_Obj && "Invalid file type!"); - MCCodeEmitter *CE = TheTarget->createCodeEmitter(*TM, Ctx); + MCCodeEmitter *CE = TheTarget->createCodeEmitter(*TM->getInstrInfo(), + STI, Ctx); TargetAsmBackend *TAB = TheTarget->createAsmBackend(Opts.Triple); Str.reset(TheTarget->createObjectStreamer(Opts.Triple, Ctx, *TAB, *Out, CE, Opts.RelaxAll, @@ -307,7 +315,8 @@ static bool ExecuteAssembler(AssemblerInvocation &Opts, Diagnostic &Diags) { OwningPtr<MCAsmParser> Parser(createMCAsmParser(*TheTarget, SrcMgr, Ctx, *Str.get(), *MAI)); - OwningPtr<TargetAsmParser> TAP(TheTarget->createAsmParser(*Parser, *TM)); + OwningPtr<TargetAsmParser> + TAP(TheTarget->createAsmParser(const_cast<MCSubtargetInfo&>(STI), *Parser)); if (!TAP) { Diags.Report(diag::err_target_unknown_triple) << Opts.Triple; return false; @@ -347,6 +356,9 @@ int cc1as_main(const char **ArgBegin, const char **ArgEnd, InitializeAllTargetInfos(); // FIXME: We shouldn't need to initialize the Target(Machine)s. InitializeAllTargets(); + InitializeAllMCAsmInfos(); + InitializeAllMCInstrInfos(); + InitializeAllMCSubtargetInfos(); InitializeAllAsmPrinters(); InitializeAllAsmParsers(); diff --git a/tools/driver/driver.cpp b/tools/driver/driver.cpp index db72da42ea34..ca8982619e94 100644 --- a/tools/driver/driver.cpp +++ b/tools/driver/driver.cpp @@ -327,9 +327,11 @@ static void ParseProgName(llvm::SmallVectorImpl<const char *> &ArgVector, std::string IgnoredError; if (llvm::TargetRegistry::lookupTarget(Prefix, IgnoredError)) { - ArgVector.insert(&ArgVector[1], - SaveStringInSet(SavedStrings, Prefix)); - ArgVector.insert(&ArgVector[1], + llvm::SmallVectorImpl<const char *>::iterator it = ArgVector.begin(); + if (it != ArgVector.end()) + ++it; + ArgVector.insert(it, SaveStringInSet(SavedStrings, Prefix)); + ArgVector.insert(it, SaveStringInSet(SavedStrings, std::string("-ccc-host-triple"))); } } diff --git a/tools/libclang/ARCMigrate.cpp b/tools/libclang/ARCMigrate.cpp new file mode 100644 index 000000000000..39c7d84d5190 --- /dev/null +++ b/tools/libclang/ARCMigrate.cpp @@ -0,0 +1,98 @@ +//===- ARCMigrate.cpp - Clang-C ARC Migration Library ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements the main API hooks in the Clang-C ARC Migration library. +// +//===----------------------------------------------------------------------===// + +#include "clang-c/Index.h" + +#include "CXString.h" +#include "clang/ARCMigrate/ARCMT.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "llvm/Support/FileSystem.h" + +using namespace clang; +using namespace arcmt; + +namespace { + +struct Remap { + std::vector<std::pair<std::string, std::string> > Vec; +}; + +} // anonymous namespace. + +//===----------------------------------------------------------------------===// +// libClang public APIs. +//===----------------------------------------------------------------------===// + +extern "C" { + +CXRemapping clang_getRemappings(const char *migrate_dir_path) { + bool Logging = ::getenv("LIBCLANG_LOGGING"); + + if (!migrate_dir_path) { + if (Logging) + llvm::errs() << "clang_getRemappings was called with NULL parameter\n"; + return 0; + } + + bool exists = false; + llvm::sys::fs::exists(migrate_dir_path, exists); + if (!exists) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappings(\"" << migrate_dir_path + << "\")\n"; + llvm::errs() << "\"" << migrate_dir_path << "\" does not exist\n"; + } + return 0; + } + + TextDiagnosticBuffer diagBuffer; + llvm::OwningPtr<Remap> remap(new Remap()); + + bool err = arcmt::getFileRemappings(remap->Vec, migrate_dir_path,&diagBuffer); + + if (err) { + if (Logging) { + llvm::errs() << "Error by clang_getRemappings(\"" << migrate_dir_path + << "\")\n"; + for (TextDiagnosticBuffer::const_iterator + I = diagBuffer.err_begin(), E = diagBuffer.err_end(); I != E; ++I) + llvm::errs() << I->second << '\n'; + } + return 0; + } + + return remap.take(); +} + +unsigned clang_remap_getNumFiles(CXRemapping map) { + return static_cast<Remap *>(map)->Vec.size(); + +} + +void clang_remap_getFilenames(CXRemapping map, unsigned index, + CXString *original, CXString *transformed) { + if (original) + *original = cxstring::createCXString( + static_cast<Remap *>(map)->Vec[index].first, + /*DupString =*/ true); + if (transformed) + *transformed = cxstring::createCXString( + static_cast<Remap *>(map)->Vec[index].second, + /*DupString =*/ true); +} + +void clang_remap_dispose(CXRemapping map) { + delete static_cast<Remap *>(map); +} + +} // end: extern "C" diff --git a/tools/libclang/CIndex.cpp b/tools/libclang/CIndex.cpp index 2a9d96db4c35..50d56fccb9c5 100644 --- a/tools/libclang/CIndex.cpp +++ b/tools/libclang/CIndex.cpp @@ -544,8 +544,8 @@ bool CursorVisitor::VisitChildren(CXCursor Cursor) { // do so. PreprocessingRecord::iterator E, EEnd; for (llvm::tie(E, EEnd) = getPreprocessedEntities(); E != EEnd; ++E) { - if (MacroInstantiation *MI = dyn_cast<MacroInstantiation>(*E)) { - if (Visit(MakeMacroInstantiationCursor(MI, tu))) + if (MacroExpansion *ME = dyn_cast<MacroExpansion>(*E)) { + if (Visit(MakeMacroExpansionCursor(ME, tu))) return true; continue; @@ -1330,6 +1330,11 @@ bool CursorVisitor::VisitTemplateName(TemplateName Name, SourceLocation Loc) { return Visit(MakeCursorTemplateRef( Name.getAsQualifiedTemplateName()->getDecl(), Loc, TU)); + + case TemplateName::SubstTemplateTemplateParm: + return Visit(MakeCursorTemplateRef( + Name.getAsSubstTemplateTemplateParm()->getParameter(), + Loc, TU)); case TemplateName::SubstTemplateTemplateParmPack: return Visit(MakeCursorTemplateRef( @@ -2374,7 +2379,8 @@ CXTranslationUnit clang_createTranslationUnit(CXIndex CIdx, unsigned clang_defaultEditingTranslationUnitOptions() { return CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_CacheCompletionResults | - CXTranslationUnit_CXXPrecompiledPreamble; + CXTranslationUnit_CXXPrecompiledPreamble | + CXTranslationUnit_CXXChainedPCH; } CXTranslationUnit @@ -2385,7 +2391,7 @@ clang_createTranslationUnitFromSourceFile(CXIndex CIdx, unsigned num_unsaved_files, struct CXUnsavedFile *unsaved_files) { unsigned Options = CXTranslationUnit_DetailedPreprocessingRecord | - CXTranslationUnit_NestedMacroInstantiations; + CXTranslationUnit_NestedMacroExpansions; return clang_parseTranslationUnit(CIdx, source_filename, command_line_args, num_command_line_args, unsaved_files, num_unsaved_files, @@ -2490,12 +2496,12 @@ static void clang_parseTranslationUnit_Impl(void *UserData) { Args->push_back(source_filename); // Do we need the detailed preprocessing record? - bool NestedMacroInstantiations = false; + bool NestedMacroExpansions = false; if (options & CXTranslationUnit_DetailedPreprocessingRecord) { Args->push_back("-Xclang"); Args->push_back("-detailed-preprocessing-record"); - NestedMacroInstantiations - = (options & CXTranslationUnit_NestedMacroInstantiations); + NestedMacroExpansions + = (options & CXTranslationUnit_NestedMacroExpansions); } unsigned NumErrors = Diags->getClient()->getNumErrors(); @@ -2515,7 +2521,7 @@ static void clang_parseTranslationUnit_Impl(void *UserData) { CacheCodeCompetionResults, CXXPrecompilePreamble, CXXChainedPCH, - NestedMacroInstantiations)); + NestedMacroExpansions)); if (NumErrors != Diags->getClient()->getNumErrors()) { // Make sure to check that 'Unit' is non-NULL. @@ -2588,9 +2594,9 @@ unsigned clang_defaultSaveOptions(CXTranslationUnit TU) { int clang_saveTranslationUnit(CXTranslationUnit TU, const char *FileName, unsigned options) { if (!TU) - return 1; + return CXSaveError_InvalidTU; - int result = static_cast<ASTUnit *>(TU->TUData)->Save(FileName); + CXSaveError result = static_cast<ASTUnit *>(TU->TUData)->Save(FileName); if (getenv("LIBCLANG_RESOURCE_USAGE")) PrintLibclangResourceUsage(TU); return result; @@ -2800,7 +2806,7 @@ void clang_getInstantiationLocation(CXSourceLocation location, *static_cast<const SourceManager*>(location.ptr_data[0]); SourceLocation InstLoc = SM.getInstantiationLoc(Loc); - // Check that the FileID is invalid on the instantiation location. + // Check that the FileID is invalid on the expansion location. // This can manifest in invalid code. FileID fileID = SM.getFileID(InstLoc); bool Invalid = false; @@ -3145,8 +3151,8 @@ CXString clang_getCursorSpelling(CXCursor C) { return createCXString(""); } - if (C.kind == CXCursor_MacroInstantiation) - return createCXString(getCursorMacroInstantiation(C)->getName() + if (C.kind == CXCursor_MacroExpansion) + return createCXString(getCursorMacroExpansion(C)->getName() ->getNameStart()); if (C.kind == CXCursor_MacroDefinition) @@ -3346,8 +3352,8 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return createCXString("preprocessing directive"); case CXCursor_MacroDefinition: return createCXString("macro definition"); - case CXCursor_MacroInstantiation: - return createCXString("macro instantiation"); + case CXCursor_MacroExpansion: + return createCXString("macro expansion"); case CXCursor_InclusionDirective: return createCXString("inclusion directive"); case CXCursor_Namespace: @@ -3392,11 +3398,34 @@ CXString clang_getCursorKindSpelling(enum CXCursorKind Kind) { return createCXString((const char*) 0); } +struct GetCursorData { + SourceLocation TokenBeginLoc; + CXCursor &BestCursor; + + GetCursorData(SourceLocation tokenBegin, CXCursor &outputCursor) + : TokenBeginLoc(tokenBegin), BestCursor(outputCursor) { } +}; + enum CXChildVisitResult GetCursorVisitor(CXCursor cursor, CXCursor parent, CXClientData client_data) { - CXCursor *BestCursor = static_cast<CXCursor *>(client_data); - + GetCursorData *Data = static_cast<GetCursorData *>(client_data); + CXCursor *BestCursor = &Data->BestCursor; + + if (clang_isExpression(cursor.kind) && + clang_isDeclaration(BestCursor->kind)) { + Decl *D = getCursorDecl(*BestCursor); + + // Avoid having the cursor of an expression replace the declaration cursor + // when the expression source range overlaps the declaration range. + // This can happen for C++ constructor expressions whose range generally + // include the variable declaration, e.g.: + // MyCXXClass foo; // Make sure pointing at 'foo' returns a VarDecl cursor. + if (D->getLocation().isValid() && Data->TokenBeginLoc.isValid() && + D->getLocation() == Data->TokenBeginLoc) + return CXChildVisit_Break; + } + // If our current best cursor is the construction of a temporary object, // don't replace that cursor with a type reference, because we want // clang_getCursor() to point at the constructor. @@ -3440,8 +3469,9 @@ CXCursor clang_getCursor(CXTranslationUnit TU, CXSourceLocation Loc) { // FIXME: Would be great to have a "hint" cursor, then walk from that // hint cursor upward until we find a cursor whose source range encloses // the region of interest, rather than starting from the translation unit. + GetCursorData ResultData(SLoc, Result); CXCursor Parent = clang_getTranslationUnitCursor(TU); - CursorVisitor CursorVis(TU, GetCursorVisitor, &Result, + CursorVisitor CursorVis(TU, GetCursorVisitor, &ResultData, Decl::MaxPCHLevel, true, SourceLocation(SLoc)); CursorVis.VisitChildren(Parent); } @@ -3532,6 +3562,10 @@ unsigned clang_isStatement(enum CXCursorKind K) { return K >= CXCursor_FirstStmt && K <= CXCursor_LastStmt; } +unsigned clang_isAttribute(enum CXCursorKind K) { + return K >= CXCursor_FirstAttr && K <= CXCursor_LastAttr; +} + unsigned clang_isTranslationUnit(enum CXCursorKind K) { return K == CXCursor_TranslationUnit; } @@ -3638,9 +3672,9 @@ CXSourceLocation clang_getCursorLocation(CXCursor C) { return cxloc::translateSourceLocation(getCursorContext(C), L); } - if (C.kind == CXCursor_MacroInstantiation) { + if (C.kind == CXCursor_MacroExpansion) { SourceLocation L - = cxcursor::getCursorMacroInstantiation(C)->getSourceRange().getBegin(); + = cxcursor::getCursorMacroExpansion(C)->getSourceRange().getBegin(); return cxloc::translateSourceLocation(getCursorContext(C), L); } @@ -3725,8 +3759,8 @@ static SourceRange getRawCursorExtent(CXCursor C) { if (C.kind == CXCursor_PreprocessingDirective) return cxcursor::getCursorPreprocessingDirective(C); - if (C.kind == CXCursor_MacroInstantiation) - return cxcursor::getCursorMacroInstantiation(C)->getSourceRange(); + if (C.kind == CXCursor_MacroExpansion) + return cxcursor::getCursorMacroExpansion(C)->getSourceRange(); if (C.kind == CXCursor_MacroDefinition) return cxcursor::getCursorMacroDefinition(C)->getSourceRange(); @@ -3842,8 +3876,8 @@ CXCursor clang_getCursorReferenced(CXCursor C) { return clang_getNullCursor(); } - if (C.kind == CXCursor_MacroInstantiation) { - if (MacroDefinition *Def = getCursorMacroInstantiation(C)->getDefinition()) + if (C.kind == CXCursor_MacroExpansion) { + if (MacroDefinition *Def = getCursorMacroExpansion(C)->getDefinition()) return MakeMacroDefinitionCursor(Def, tu); } @@ -3911,7 +3945,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { WasReference = true; } - if (C.kind == CXCursor_MacroInstantiation) + if (C.kind == CXCursor_MacroExpansion) return clang_getCursorReferenced(C); if (!clang_isDeclaration(C.kind)) @@ -4107,8 +4141,17 @@ CXCursor clang_getCanonicalCursor(CXCursor C) { if (!clang_isDeclaration(C.kind)) return C; - if (Decl *D = getCursorDecl(C)) + if (Decl *D = getCursorDecl(C)) { + if (ObjCCategoryImplDecl *CatImplD = dyn_cast<ObjCCategoryImplDecl>(D)) + if (ObjCCategoryDecl *CatD = CatImplD->getCategoryDecl()) + return MakeCXCursor(CatD, getCursorTU(C)); + + if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D)) + if (ObjCInterfaceDecl *IFD = ImplD->getClassInterface()) + return MakeCXCursor(IFD, getCursorTU(C)); + return MakeCXCursor(D->getCanonicalDecl(), getCursorTU(C)); + } return C; } @@ -4507,9 +4550,9 @@ AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) { } if (clang_isPreprocessing(cursor.kind)) { - // For macro instantiations, just note where the beginning of the macro - // instantiation occurs. - if (cursor.kind == CXCursor_MacroInstantiation) { + // For macro expansions, just note where the beginning of the macro + // expansion occurs. + if (cursor.kind == CXCursor_MacroExpansion) { Annotated[Loc.int_data] = cursor; return CXChildVisit_Recurse; } @@ -4626,6 +4669,24 @@ AnnotateTokensWorker::Visit(CXCursor cursor, CXCursor parent) { break; } + // Avoid having the cursor of an expression "overwrite" the annotation of the + // variable declaration that it belongs to. + // This can happen for C++ constructor expressions whose range generally + // include the variable declaration, e.g.: + // MyCXXClass foo; // Make sure we don't annotate 'foo' as a CallExpr cursor. + if (clang_isExpression(cursorK)) { + Expr *E = getCursorExpr(cursor); + if (Decl *D = getCursorParentDecl(cursor)) { + const unsigned I = NextToken(); + if (E->getLocStart().isValid() && D->getLocation().isValid() && + E->getLocStart() == D->getLocation() && + E->getLocStart() == GetTokenLoc(I)) { + Cursors[I] = updateC; + AdvanceToken(); + } + } + } + // Visit children to get their cursor information. const unsigned BeforeChildren = NextToken(); VisitChildren(cursor); @@ -4797,6 +4858,7 @@ static void clang_annotateTokensImpl(void *UserData) { llvm::StringSwitch<bool>(II->getName()) .Case("readonly", true) .Case("assign", true) + .Case("unsafe_unretained", true) .Case("readwrite", true) .Case("retain", true) .Case("copy", true) @@ -4804,6 +4866,8 @@ static void clang_annotateTokensImpl(void *UserData) { .Case("atomic", true) .Case("getter", true) .Case("setter", true) + .Case("strong", true) + .Case("weak", true) .Default(false)) Tokens[I].int_data[0] = CXToken_Keyword; } @@ -5354,10 +5418,9 @@ CXTUResourceUsage clang_getCXTUResourceUsage(CXTranslationUnit TU) { // How much memory is being used by the Preprocessor? Preprocessor &pp = astUnit->getPreprocessor(); - const llvm::BumpPtrAllocator &ppAlloc = pp.getPreprocessorAllocator(); createCXTUResourceUsageEntry(*entries, CXTUResourceUsage_Preprocessor, - ppAlloc.getTotalMemory()); + pp.getTotalMemory()); if (PreprocessingRecord *pRec = pp.getPreprocessingRecord()) { createCXTUResourceUsageEntry(*entries, diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp index 0c8317e4461f..832e2f2f7147 100644 --- a/tools/libclang/CIndexCodeCompletion.cpp +++ b/tools/libclang/CIndexCodeCompletion.cpp @@ -235,6 +235,13 @@ struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { /// \brief Allocator used to store code completion results. clang::CodeCompletionAllocator CodeCompletionAllocator; + + /// \brief Context under which completion occurred. + enum clang::CodeCompletionContext::Kind ContextKind; + + /// \brief A bitfield representing the acceptable completions for the + /// current context. + unsigned long long Contexts; }; /// \brief Tracks the number of code-completion result objects that are @@ -273,6 +280,177 @@ AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { } // end extern "C" +static unsigned long long getContextsForContextKind( + enum CodeCompletionContext::Kind kind, + Sema &S) { + unsigned long long contexts = 0; + switch (kind) { + case CodeCompletionContext::CCC_OtherWithMacros: { + //We can allow macros here, but we don't know what else is permissible + //So we'll say the only thing permissible are macros + contexts = CXCompletionContext_MacroName; + break; + } + case CodeCompletionContext::CCC_TopLevel: + case CodeCompletionContext::CCC_ObjCIvarList: + case CodeCompletionContext::CCC_ClassStructUnion: + case CodeCompletionContext::CCC_Type: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface; + if (S.getLangOptions().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_Statement: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_AnyValue; + if (S.getLangOptions().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_Expression: { + contexts = CXCompletionContext_AnyValue; + if (S.getLangOptions().CPlusPlus) { + contexts |= CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_ObjCMessageReceiver: { + contexts = CXCompletionContext_ObjCObjectValue | + CXCompletionContext_ObjCSelectorValue | + CXCompletionContext_ObjCInterface; + if (S.getLangOptions().CPlusPlus) { + contexts |= CXCompletionContext_CXXClassTypeValue | + CXCompletionContext_AnyType | + CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_DotMemberAccess: { + contexts = CXCompletionContext_DotMemberAccess; + break; + } + case CodeCompletionContext::CCC_ArrowMemberAccess: { + contexts = CXCompletionContext_ArrowMemberAccess; + break; + } + case CodeCompletionContext::CCC_ObjCPropertyAccess: { + contexts = CXCompletionContext_ObjCPropertyAccess; + break; + } + case CodeCompletionContext::CCC_EnumTag: { + contexts = CXCompletionContext_EnumTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_UnionTag: { + contexts = CXCompletionContext_UnionTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_ClassOrStructTag: { + contexts = CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_ObjCProtocolName: { + contexts = CXCompletionContext_ObjCProtocol; + break; + } + case CodeCompletionContext::CCC_Namespace: { + contexts = CXCompletionContext_Namespace; + break; + } + case CodeCompletionContext::CCC_PotentiallyQualifiedName: { + contexts = CXCompletionContext_NestedNameSpecifier; + break; + } + case CodeCompletionContext::CCC_MacroNameUse: { + contexts = CXCompletionContext_MacroName; + break; + } + case CodeCompletionContext::CCC_NaturalLanguage: { + contexts = CXCompletionContext_NaturalLanguage; + break; + } + case CodeCompletionContext::CCC_SelectorName: { + contexts = CXCompletionContext_ObjCSelectorName; + break; + } + case CodeCompletionContext::CCC_ParenthesizedExpression: { + contexts = CXCompletionContext_AnyType | + CXCompletionContext_ObjCInterface | + CXCompletionContext_AnyValue; + if (S.getLangOptions().CPlusPlus) { + contexts |= CXCompletionContext_EnumTag | + CXCompletionContext_UnionTag | + CXCompletionContext_StructTag | + CXCompletionContext_ClassTag | + CXCompletionContext_NestedNameSpecifier; + } + break; + } + case CodeCompletionContext::CCC_ObjCInstanceMessage: { + contexts = CXCompletionContext_ObjCInstanceMessage; + break; + } + case CodeCompletionContext::CCC_ObjCClassMessage: { + contexts = CXCompletionContext_ObjCClassMessage; + break; + } + case CodeCompletionContext::CCC_ObjCSuperclass: { + contexts = CXCompletionContext_ObjCInterface; + break; + } + case CodeCompletionContext::CCC_ObjCCategoryName: { + contexts = CXCompletionContext_ObjCCategory; + break; + } + case CodeCompletionContext::CCC_Other: + case CodeCompletionContext::CCC_ObjCInterface: + case CodeCompletionContext::CCC_ObjCImplementation: + case CodeCompletionContext::CCC_Name: + case CodeCompletionContext::CCC_MacroName: + case CodeCompletionContext::CCC_PreprocessorExpression: + case CodeCompletionContext::CCC_PreprocessorDirective: + case CodeCompletionContext::CCC_TypeQualifiers: { + //Only Clang results should be accepted, so we'll set all of the other + //context bits to 0 (i.e. the empty set) + contexts = CXCompletionContext_Unexposed; + break; + } + case CodeCompletionContext::CCC_Recovery: { + //We don't know what the current context is, so we'll return unknown + //This is the equivalent of setting all of the other context bits + contexts = CXCompletionContext_Unknown; + break; + } + } + return contexts; +} + namespace { class CaptureCompletionResults : public CodeCompleteConsumer { AllocatedCXCodeCompleteResults &AllocatedResults; @@ -298,6 +476,11 @@ namespace { R.CompletionString = StoredCompletion; StoredResults.push_back(R); } + + enum CodeCompletionContext::Kind kind = Context.getKind(); + + AllocatedResults.ContextKind = kind; + AllocatedResults.Contexts = getContextsForContextKind(kind, S); } virtual void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, @@ -538,6 +721,15 @@ clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, return new CXStoredDiagnostic(Results->Diagnostics[Index], Results->LangOpts); } +unsigned long long +clang_codeCompleteGetContexts(CXCodeCompleteResults *ResultsIn) { + AllocatedCXCodeCompleteResults *Results + = static_cast<AllocatedCXCodeCompleteResults*>(ResultsIn); + if (!Results) + return 0; + + return Results->Contexts; +} } // end extern "C" diff --git a/tools/libclang/CMakeLists.txt b/tools/libclang/CMakeLists.txt index 7a6270d17647..f45389f0b9f4 100644 --- a/tools/libclang/CMakeLists.txt +++ b/tools/libclang/CMakeLists.txt @@ -1,4 +1,6 @@ set(LLVM_USED_LIBS + clangARCMigrate + clangRewrite clangFrontend clangDriver clangSerialization @@ -14,6 +16,7 @@ set( LLVM_LINK_COMPONENTS ) set(SOURCES + ARCMigrate.cpp CIndex.cpp CIndexCXX.cpp CIndexCodeCompletion.cpp diff --git a/tools/libclang/CXCursor.cpp b/tools/libclang/CXCursor.cpp index b34370d2e652..b3c57dc9344f 100644 --- a/tools/libclang/CXCursor.cpp +++ b/tools/libclang/CXCursor.cpp @@ -92,6 +92,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::ObjCAtFinallyStmtClass: case Stmt::ObjCAtThrowStmtClass: case Stmt::ObjCAtSynchronizedStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: case Stmt::ObjCForCollectionStmtClass: case Stmt::CXXCatchStmtClass: case Stmt::CXXTryStmtClass: @@ -99,6 +100,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::SEHTryStmtClass: case Stmt::SEHExceptStmtClass: case Stmt::SEHFinallyStmtClass: + case Stmt::MaterializeTemporaryExprClass: K = CXCursor_UnexposedStmt; break; @@ -167,7 +169,9 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::ObjCEncodeExprClass: case Stmt::ObjCSelectorExprClass: case Stmt::ObjCProtocolExprClass: - case Stmt::ObjCIsaExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCIndirectCopyRestoreExprClass: + case Stmt::ObjCBridgedCastExprClass: case Stmt::ShuffleVectorExprClass: case Stmt::BlockExprClass: case Stmt::OpaqueValueExprClass: @@ -179,6 +183,7 @@ CXCursor cxcursor::MakeCXCursor(Stmt *S, Decl *Parent, case Stmt::DeclRefExprClass: case Stmt::BlockDeclRefExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: case Stmt::SubstNonTypeTemplateParmPackExprClass: // FIXME: UnresolvedLookupExpr? // FIXME: DependentScopeDeclRefExpr? @@ -374,15 +379,15 @@ MacroDefinition *cxcursor::getCursorMacroDefinition(CXCursor C) { return static_cast<MacroDefinition *>(C.data[0]); } -CXCursor cxcursor::MakeMacroInstantiationCursor(MacroInstantiation *MI, - CXTranslationUnit TU) { - CXCursor C = { CXCursor_MacroInstantiation, { MI, 0, TU } }; +CXCursor cxcursor::MakeMacroExpansionCursor(MacroExpansion *MI, + CXTranslationUnit TU) { + CXCursor C = { CXCursor_MacroExpansion, { MI, 0, TU } }; return C; } -MacroInstantiation *cxcursor::getCursorMacroInstantiation(CXCursor C) { - assert(C.kind == CXCursor_MacroInstantiation); - return static_cast<MacroInstantiation *>(C.data[0]); +MacroExpansion *cxcursor::getCursorMacroExpansion(CXCursor C) { + assert(C.kind == CXCursor_MacroExpansion); + return static_cast<MacroExpansion *>(C.data[0]); } CXCursor cxcursor::MakeInclusionDirectiveCursor(InclusionDirective *ID, @@ -480,6 +485,10 @@ Attr *cxcursor::getCursorAttr(CXCursor Cursor) { return (Attr *)Cursor.data[1]; } +Decl *cxcursor::getCursorParentDecl(CXCursor Cursor) { + return (Decl *)Cursor.data[0]; +} + ASTContext &cxcursor::getCursorContext(CXCursor Cursor) { return getCursorASTUnit(Cursor)->getASTContext(); } diff --git a/tools/libclang/CXCursor.h b/tools/libclang/CXCursor.h index 11f2500fb144..68d09e76c1c0 100644 --- a/tools/libclang/CXCursor.h +++ b/tools/libclang/CXCursor.h @@ -31,7 +31,7 @@ class FieldDecl; class InclusionDirective; class LabelStmt; class MacroDefinition; -class MacroInstantiation; +class MacroExpansion; class NamedDecl; class ObjCInterfaceDecl; class ObjCProtocolDecl; @@ -134,13 +134,13 @@ CXCursor MakeMacroDefinitionCursor(MacroDefinition *, CXTranslationUnit TU); /// source range. MacroDefinition *getCursorMacroDefinition(CXCursor C); -/// \brief Create a macro instantiation cursor. -CXCursor MakeMacroInstantiationCursor(MacroInstantiation *, - CXTranslationUnit TU); +/// \brief Create a macro expansion cursor. +CXCursor MakeMacroExpansionCursor(MacroExpansion *, + CXTranslationUnit TU); -/// \brief Unpack a given macro instantiation cursor to retrieve its +/// \brief Unpack a given macro expansion cursor to retrieve its /// source range. -MacroInstantiation *getCursorMacroInstantiation(CXCursor C); +MacroExpansion *getCursorMacroExpansion(CXCursor C); /// \brief Create an inclusion directive cursor. CXCursor MakeInclusionDirectiveCursor(InclusionDirective *, @@ -184,6 +184,7 @@ Decl *getCursorDecl(CXCursor Cursor); Expr *getCursorExpr(CXCursor Cursor); Stmt *getCursorStmt(CXCursor Cursor); Attr *getCursorAttr(CXCursor Cursor); +Decl *getCursorParentDecl(CXCursor Cursor); ASTContext &getCursorContext(CXCursor Cursor); ASTUnit *getCursorASTUnit(CXCursor Cursor); diff --git a/tools/libclang/CXType.cpp b/tools/libclang/CXType.cpp index 5af955357bb2..45c73468d971 100644 --- a/tools/libclang/CXType.cpp +++ b/tools/libclang/CXType.cpp @@ -366,7 +366,11 @@ unsigned clang_isPODType(CXType X) { QualType T = GetQualType(X); if (!T.getTypePtrOrNull()) return 0; - return T->isPODType() ? 1 : 0; + + CXTranslationUnit TU = GetTU(X); + ASTUnit *AU = static_cast<ASTUnit*>(TU->TUData); + + return T.isPODType(AU->getASTContext()) ? 1 : 0; } CXString clang_getDeclObjCTypeEncoding(CXCursor C) { diff --git a/tools/libclang/Makefile b/tools/libclang/Makefile index e684652aa4ca..375f7f20fe00 100644 --- a/tools/libclang/Makefile +++ b/tools/libclang/Makefile @@ -16,8 +16,9 @@ LINK_LIBS_IN_SHARED = 1 SHARED_LIBRARY = 1 LINK_COMPONENTS := support mc -USEDLIBS = clangFrontend.a clangDriver.a clangSerialization.a clangParse.a \ - clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a +USEDLIBS = clangARCMigrate.a clangRewrite.a clangFrontend.a clangDriver.a \ + clangSerialization.a \ + clangParse.a clangSema.a clangAnalysis.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/tools/libclang/libclang.darwin.exports b/tools/libclang/libclang.darwin.exports index 85dfcb68685c..bfc5be9c078b 100644 --- a/tools/libclang/libclang.darwin.exports +++ b/tools/libclang/libclang.darwin.exports @@ -6,6 +6,7 @@ _clang_annotateTokens _clang_codeCompleteAt _clang_codeCompleteGetDiagnostic _clang_codeCompleteGetNumDiagnostics +_clang_codeCompleteGetContexts _clang_constructUSR_ObjCCategory _clang_constructUSR_ObjCClass _clang_constructUSR_ObjCIvar @@ -112,6 +113,7 @@ _clang_getTranslationUnitSpelling _clang_getTypeDeclaration _clang_getTypeKindSpelling _clang_hashCursor +_clang_isAttribute _clang_isConstQualifiedType _clang_isCursorDefinition _clang_isDeclaration @@ -135,3 +137,7 @@ _clang_toggleCrashRecovery _clang_tokenize _clang_visitChildren _clang_visitChildrenWithBlock +_clang_getRemappings +_clang_remap_getNumFiles +_clang_remap_getFilenames +_clang_remap_dispose diff --git a/tools/libclang/libclang.exports b/tools/libclang/libclang.exports index 403cd67538c5..47b703011c76 100644 --- a/tools/libclang/libclang.exports +++ b/tools/libclang/libclang.exports @@ -6,6 +6,7 @@ clang_annotateTokens clang_codeCompleteAt clang_codeCompleteGetDiagnostic clang_codeCompleteGetNumDiagnostics +clang_codeCompleteGetContexts clang_constructUSR_ObjCCategory clang_constructUSR_ObjCClass clang_constructUSR_ObjCIvar @@ -112,6 +113,7 @@ clang_getTranslationUnitSpelling clang_getTypeDeclaration clang_getTypeKindSpelling clang_hashCursor +clang_isAttribute clang_isConstQualifiedType clang_isCursorDefinition clang_isDeclaration @@ -135,3 +137,7 @@ clang_toggleCrashRecovery clang_tokenize clang_visitChildren clang_visitChildrenWithBlock +clang_getRemappings +clang_remap_getNumFiles +clang_remap_getFilenames +clang_remap_dispose diff --git a/tools/scan-build/ccc-analyzer b/tools/scan-build/ccc-analyzer index 7793a8db49b1..5697b214a2d7 100755 --- a/tools/scan-build/ccc-analyzer +++ b/tools/scan-build/ccc-analyzer @@ -365,11 +365,9 @@ my %LangMap = ( 'cp' => 'c++', 'cpp' => 'c++', 'cc' => 'c++', - 'ii' => 'c++', 'i' => 'c-cpp-output', 'm' => 'objective-c', - 'mi' => 'objective-c-cpp-output', - 'mm' => 'objective-c++' + 'mi' => 'objective-c-cpp-output' ); my %UniqueOptions = ( @@ -382,11 +380,14 @@ my %UniqueOptions = ( my %LangsAccepted = ( "objective-c" => 1, - "c" => 1, - "c++" => 1, - "objective-c++" => 1 + "c" => 1 ); +if (defined $ENV{'CCC_ANALYZER_CPLUSPLUS'}) { + $LangsAccepted{"c++"} = 1; + $LangsAccepted{"objective-c++"} = 1; +} + ##----------------------------------------------------------------------------## # Main Logic. ##----------------------------------------------------------------------------## @@ -620,9 +621,9 @@ if ($Action eq 'compile' or $Action eq 'link') { push @AnalyzeArgs, "-analyzer-constraints=$ConstraintsModel"; } - if (defined $Analyses) { - push @AnalyzeArgs, split '\s+', $Analyses; - } +# if (defined $Analyses) { +# push @AnalyzeArgs, split '\s+', $Analyses; +# } if (defined $OutputFormat) { push @AnalyzeArgs, "-analyzer-output=" . $OutputFormat; diff --git a/unittests/Basic/FileManagerTest.cpp b/unittests/Basic/FileManagerTest.cpp index 80727790d038..a725a437f6b7 100644 --- a/unittests/Basic/FileManagerTest.cpp +++ b/unittests/Basic/FileManagerTest.cpp @@ -30,7 +30,7 @@ private: void InjectFileOrDirectory(const char *Path, ino_t INode, bool IsFile) { struct stat statBuf = {}; statBuf.st_dev = 1; -#ifndef LLVM_ON_WIN32 // struct stat has no st_ino field on Windows. +#ifndef _WIN32 // struct stat has no st_ino field on Windows. statBuf.st_ino = INode; #endif statBuf.st_mode = IsFile ? (0777 | S_IFREG) // a regular file @@ -187,7 +187,7 @@ TEST_F(FileManagerTest, getFileReturnsNULLForNonexistentFile) { // The following tests apply to Unix-like system only. -#ifndef LLVM_ON_WIN32 +#ifndef _WIN32 // getFile() returns the same FileEntry for real files that are aliases. TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedRealFiles) { @@ -217,6 +217,6 @@ TEST_F(FileManagerTest, getFileReturnsSameFileEntryForAliasedVirtualFiles) { EXPECT_EQ(manager.getFile("abc/foo.cpp"), manager.getFile("abc/bar.cpp")); } -#endif // !LLVM_ON_WIN32 +#endif // !_WIN32 } // anonymous namespace diff --git a/unittests/Frontend/Makefile b/unittests/Frontend/Makefile index 4d9937f517cf..7ec2b894a04b 100644 --- a/unittests/Frontend/Makefile +++ b/unittests/Frontend/Makefile @@ -13,7 +13,7 @@ LINK_COMPONENTS := support mc USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a \ - clangAnalysis.a clangIndex.a clangRewrite.a \ - clangAST.a clangLex.a clangBasic.a + clangIndex.a clangARCMigrate.a clangRewrite.a \ + clangAnalysis.a clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/unittests/Makefile diff --git a/www/compatibility.html b/www/compatibility.html index b68a7663d4fb..783758f3ac80 100644 --- a/www/compatibility.html +++ b/www/compatibility.html @@ -459,7 +459,7 @@ int main() { <p>Clang complains: -<pre> <b>my_file.cpp:2:10: <span class="error">error:</span> call to function 'Multiply' that is neither visible in the template definition nor found by argument dependent lookup</b> +<pre> <b>my_file.cpp:2:10: <span class="error">error:</span> call to function 'Multiply' that is neither visible in the template definition nor found by argument-dependent lookup</b> return Multiply(x, x); <span class="caret"> ^</span> <b>my_file.cpp:10:3: <span class="note">note:</span> in instantiation of function template specialization 'Squared<int>' requested here</b> @@ -520,7 +520,7 @@ void Use() { <p>Again, Clang complains:</p> -<pre> <b>my_file2.cpp:5:13: <span class="error">error:</span> call to function 'operator<<' that is neither visible in the template definition nor found by argument dependent lookup</b> +<pre> <b>my_file2.cpp:5:13: <span class="error">error:</span> call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup</b> std::cout << value << "\n"; <span class="caret"> ^</span> <b>my_file2.cpp:17:3: <span class="error">note:</span> in instantiation of function template specialization 'Dump<ns::Data>' requested here</b> |