[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
This revision was automatically updated to reflect the committed changes. Closed by commit rL369098: [ASTImporter] Import ctor initializers after setting flags. (authored by balazske, committed by ). Herald added a project: LLVM. Herald added a subscriber: llvm-commits. Changed prior to commit: https://reviews.llvm.org/D65935?vs=215056=215567#toc Repository: rL LLVM CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65935/new/ https://reviews.llvm.org/D65935 Files: cfe/trunk/lib/AST/ASTImporter.cpp cfe/trunk/test/Analysis/Inputs/ctu-other.cpp cfe/trunk/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt cfe/trunk/test/Analysis/ctu-main.cpp Index: cfe/trunk/lib/AST/ASTImporter.cpp === --- cfe/trunk/lib/AST/ASTImporter.cpp +++ cfe/trunk/lib/AST/ASTImporter.cpp @@ -3272,23 +3272,6 @@ // decl and its redeclarations may be required. } - // Import Ctor initializers. - if (auto *FromConstructor = dyn_cast(D)) { -if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { - SmallVector CtorInitializers(NumInitializers); - // Import first, then allocate memory and copy if there was no error. - if (Error Err = ImportContainerChecked( - FromConstructor->inits(), CtorInitializers)) -return std::move(Err); - auto **Memory = - new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; - std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); - auto *ToCtor = cast(ToFunction); - ToCtor->setCtorInitializers(Memory); - ToCtor->setNumCtorInitializers(NumInitializers); -} - } - ToFunction->setQualifierInfo(ToQualifierLoc); ToFunction->setAccess(D->getAccess()); ToFunction->setLexicalDeclContext(LexicalDC); @@ -3332,6 +3315,23 @@ return ToFTOrErr.takeError(); } + // Import Ctor initializers. + if (auto *FromConstructor = dyn_cast(D)) { +if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { + SmallVector CtorInitializers(NumInitializers); + // Import first, then allocate memory and copy if there was no error. + if (Error Err = ImportContainerChecked( + FromConstructor->inits(), CtorInitializers)) +return std::move(Err); + auto **Memory = + new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; + std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); + auto *ToCtor = cast(ToFunction); + ToCtor->setCtorInitializers(Memory); + ToCtor->setNumCtorInitializers(NumInitializers); +} + } + if (D->doesThisDeclarationHaveABody()) { Error Err = ImportFunctionDeclBody(D, ToFunction); Index: cfe/trunk/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt === --- cfe/trunk/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt +++ cfe/trunk/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt @@ -27,3 +27,4 @@ c:@extU ctu-other.cpp.ast c:@S@TestAnonUnionUSR@Test ctu-other.cpp.ast c:@F@testImportOfIncompleteDefaultParmDuringImport#I# ctu-other.cpp.ast +c:@F@testImportOfDelegateConstructor#I# ctu-other.cpp.ast \ No newline at end of file Index: cfe/trunk/test/Analysis/Inputs/ctu-other.cpp === --- cfe/trunk/test/Analysis/Inputs/ctu-other.cpp +++ cfe/trunk/test/Analysis/Inputs/ctu-other.cpp @@ -164,3 +164,14 @@ int DefaultParmContext::f() { return fDefaultParm(); } + +class TestDelegateConstructor { +public: + TestDelegateConstructor() : TestDelegateConstructor(2) {} + TestDelegateConstructor(int) {} +}; + +int testImportOfDelegateConstructor(int i) { + TestDelegateConstructor TDC; + return i; +} Index: cfe/trunk/test/Analysis/ctu-main.cpp === --- cfe/trunk/test/Analysis/ctu-main.cpp +++ cfe/trunk/test/Analysis/ctu-main.cpp @@ -127,6 +127,8 @@ extern int testImportOfIncompleteDefaultParmDuringImport(int); +extern int testImportOfDelegateConstructor(int); + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -163,4 +165,6 @@ clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // expected-warning{{TRUE}} clang_analyzer_eval(testImportOfIncompleteDefaultParmDuringImport(9) == 9); // expected-warning{{TRUE}} + + clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); // expected-warning{{TRUE}} } ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
balazske added a comment. LLDB probably does the import in other way. CTU test fails without the fix: FAIL: Clang :: Analysis/ctu-main.cpp (540 of 15341) TEST 'Clang :: Analysis/ctu-main.cpp' FAILED Script: -- : 'RUN: at line 1'; rm -rf build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp && mkdir build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp : 'RUN: at line 2'; mkdir -p build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir : 'RUN: at line 3'; build/Release/bin/clang -cc1 -internal-isystem build/Release/lib/clang/10.0.0/include -nostdsysteminc -triple x86_64-pc-linux-gnu-emit-pch -o build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir/ctu-other.cpp.ast llvm-project/clang/test/Analysis/Inputs/ctu-other.cpp : 'RUN: at line 5'; build/Release/bin/clang -cc1 -internal-isystem build/Release/lib/clang/10.0.0/include -nostdsysteminc -triple x86_64-pc-linux-gnu-emit-pch -o build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir/ctu-chain.cpp.ast llvm-project/clang/test/Analysis/Inputs/ctu-chain.cpp : 'RUN: at line 7'; cp llvm-project/clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir/externalDefMap.txt : 'RUN: at line 8'; build/Release/bin/clang -cc1 -internal-isystem build/Release/lib/clang/10.0.0/include -nostdsysteminc -analyze -analyzer-constraints=range -triple x86_64-pc-linux-gnu -analyzer-checker=core,debug.ExprInspection-analyzer-config experimental-enable-naive-ctu-analysis=true-analyzer-config ctu-dir=build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir -verify llvm-project/clang/test/Analysis/ctu-main.cpp : 'RUN: at line 13'; build/Release/bin/clang -cc1 -internal-isystem build/Release/lib/clang/10.0.0/include -nostdsysteminc -analyze -analyzer-constraints=range -triple x86_64-pc-linux-gnu -analyzer-checker=core,debug.ExprInspection-analyzer-config experimental-enable-naive-ctu-analysis=true-analyzer-config ctu-dir=build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir -analyzer-config display-ctu-progress=true 2>&1 llvm-project/clang/test/Analysis/ctu-main.cpp | build/Release/bin/FileCheck llvm-project/clang/test/Analysis/ctu-main.cpp -- Exit Code: 134 Command Output (stderr): -- clang: llvm-project/clang/lib/AST/DeclBase.cpp:939: bool clang::Decl::AccessDeclContextSanity() const: Assertion `Access != AS_none && "Access specifier is AS_none inside a record decl"' failed. Stack dump: 0. Program arguments: build/Release/bin/clang -cc1 -internal-isystem build/Release/lib/clang/10.0.0/include -nostdsysteminc -analyze -analyzer-constraints=range -triple x86_64-pc-linux-gnu -analyzer-checker=core,debug.ExprInspection -analyzer-config experimental-enable-naive-ctu-analysis=true -analyzer-config ctu-dir=build/Release/tools/clang/test/Analysis/Output/ctu-main.cpp.tmp/ctudir -verify llvm-project/clang/test/Analysis/ctu-main.cpp 1. parser at end of file 2. While analyzing stack: #0 Calling main 3. llvm-project/clang/test/Analysis/ctu-main.cpp:164:23: Error evaluating statement 4. llvm-project/clang/test/Analysis/ctu-main.cpp:164:23: Error evaluating statement #0 0x7f30c91a5efa llvm::sys::PrintStackTrace(llvm::raw_ostream&) (build/Release/lib/libLLVMSupport.so.10svn+0x191efa) #1 0x7f30c91a3bd4 llvm::sys::RunSignalHandlers() (build/Release/lib/libLLVMSupport.so.10svn+0x18fbd4) #2 0x7f30c91a3d12 SignalHandler(int) (build/Release/lib/libLLVMSupport.so.10svn+0x18fd12) #3 0x7f30c6347f20 (/lib/x86_64-linux-gnu/libc.so.6+0x3ef20) #4 0x7f30c6347e97 raise /build/glibc-OTsEL5/glibc-2.27/signal/../sysdeps/unix/sysv/linux/raise.c:51:0 #5 0x7f30c6349801 abort /build/glibc-OTsEL5/glibc-2.27/stdlib/abort.c:81:0 #6 0x7f30c633939a __assert_fail_base /build/glibc-OTsEL5/glibc-2.27/assert/assert.c:89:0 #7 0x7f30c6339412 (/lib/x86_64-linux-gnu/libc.so.6+0x30412) #8 0x7f30c24ad3fc (build/Release/lib/libclangAST.so.10svn+0x27c3fc) #9 0x7f30c23c1f49 clang::Decl::getAccess() const (build/Release/lib/libclangAST.so.10svn+0x190f49) #10 0x7f30c2309f32 IsStructurallyEquivalent(clang::StructuralEquivalenceContext&, clang::CXXMethodDecl*, clang::CXXMethodDecl*) (build/Release/lib/libclangAST.so.10svn+0xd8f32) #11 0x7f30c243cda9 clang::StructuralEquivalenceContext::CheckKindSpecificEquivalence(clang::Decl*, clang::Decl*) (build/Release/lib/libclangAST.so.10svn+0x20bda9) #12 0x7f30c243d046 clang::StructuralEquivalenceContext::Finish() (build/Release/lib/libclangAST.so.10svn+0x20c046) #13 0x7f30c243da28 clang::StructuralEquivalenceContext::IsEquivalent(clang::Decl*, clang::Decl*) (build/Release/lib/libclangAST.so.10svn+0x20ca28) #14
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
shafik accepted this revision. shafik added a comment. This revision is now accepted and ready to land. I was hoping to be able reproduce this in LLDB via an expression like this: expr testImportOfDelegateConstructor(10) == 10 but it does not. I am assuming the test ctu test case invokes the issues without the patch? I wonder why we don't also see it in as well. Otherwise LGTM. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65935/new/ https://reviews.llvm.org/D65935 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
balazske updated this revision to Diff 215056. balazske added a comment. - Moved 'ctor initializer' import before import of body. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65935/new/ https://reviews.llvm.org/D65935 Files: clang/lib/AST/ASTImporter.cpp clang/test/Analysis/Inputs/ctu-other.cpp clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt clang/test/Analysis/ctu-main.cpp Index: clang/test/Analysis/ctu-main.cpp === --- clang/test/Analysis/ctu-main.cpp +++ clang/test/Analysis/ctu-main.cpp @@ -125,6 +125,8 @@ static const int Test; }; +extern int testImportOfDelegateConstructor(int); + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -158,4 +160,6 @@ // clang_analyzer_eval(extSCC.a == 7); // TODO clang_analyzer_eval(extU.a == 4); // expected-warning{{TRUE}} clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // expected-warning{{TRUE}} + + clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); // expected-warning{{TRUE}} } Index: clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt === --- clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt +++ clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt @@ -26,3 +26,4 @@ c:@extSCC ctu-other.cpp.ast c:@extU ctu-other.cpp.ast c:@S@TestAnonUnionUSR@Test ctu-other.cpp.ast +c:@F@testImportOfDelegateConstructor#I# ctu-other.cpp.ast Index: clang/test/Analysis/Inputs/ctu-other.cpp === --- clang/test/Analysis/Inputs/ctu-other.cpp +++ clang/test/Analysis/Inputs/ctu-other.cpp @@ -145,3 +145,14 @@ static const int Test; }; const int TestAnonUnionUSR::Test = 5; + +class TestDelegateConstructor { +public: + TestDelegateConstructor() : TestDelegateConstructor(2) {} + TestDelegateConstructor(int) {} +}; + +int testImportOfDelegateConstructor(int i) { + TestDelegateConstructor TDC; + return i; +} Index: clang/lib/AST/ASTImporter.cpp === --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3265,23 +3265,6 @@ // decl and its redeclarations may be required. } - // Import Ctor initializers. - if (auto *FromConstructor = dyn_cast(D)) { -if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { - SmallVector CtorInitializers(NumInitializers); - // Import first, then allocate memory and copy if there was no error. - if (Error Err = ImportContainerChecked( - FromConstructor->inits(), CtorInitializers)) -return std::move(Err); - auto **Memory = - new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; - std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); - auto *ToCtor = cast(ToFunction); - ToCtor->setCtorInitializers(Memory); - ToCtor->setNumCtorInitializers(NumInitializers); -} - } - ToFunction->setQualifierInfo(ToQualifierLoc); ToFunction->setAccess(D->getAccess()); ToFunction->setLexicalDeclContext(LexicalDC); @@ -3322,6 +3305,23 @@ return ToFTOrErr.takeError(); } + // Import Ctor initializers. + if (auto *FromConstructor = dyn_cast(D)) { +if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { + SmallVector CtorInitializers(NumInitializers); + // Import first, then allocate memory and copy if there was no error. + if (Error Err = ImportContainerChecked( + FromConstructor->inits(), CtorInitializers)) +return std::move(Err); + auto **Memory = + new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; + std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); + auto *ToCtor = cast(ToFunction); + ToCtor->setCtorInitializers(Memory); + ToCtor->setNumCtorInitializers(NumInitializers); +} + } + if (D->doesThisDeclarationHaveABody()) { Error Err = ImportFunctionDeclBody(D, ToFunction); Index: clang/test/Analysis/ctu-main.cpp === --- clang/test/Analysis/ctu-main.cpp +++ clang/test/Analysis/ctu-main.cpp @@ -125,6 +125,8 @@ static const int Test; }; +extern int testImportOfDelegateConstructor(int); + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -158,4 +160,6 @@ // clang_analyzer_eval(extSCC.a == 7); // TODO clang_analyzer_eval(extU.a == 4); // expected-warning{{TRUE}} clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // expected-warning{{TRUE}} + + clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); // expected-warning{{TRUE}} }
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
a_sidorin added inline comments. Comment at: clang/lib/AST/ASTImporter.cpp:3293 + // Import Ctor initializers. + if (auto *FromConstructor = dyn_cast(D)) { I suggest to move it closer to the function body import because import of ctor initializers is a part of function body import in fact. Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65935/new/ https://reviews.llvm.org/D65935 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
balazske added a comment. In this case the problem was that some of the flags of the already created and inserted `ToFunction` are not initialized. During the import of some "ctor initializers" this non-complete ToFunction may be accessed somehow (by structural equivalence or other code) and an assert comes because invalid value. (Maybe in the test code the second constructor is imported as "ctor initializer" during the import of the first.) Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65935/new/ https://reviews.llvm.org/D65935 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
a_sidorin added a comment. Hello Balazs, Do I understand correctly that it was unset `ToFunction->setLexicalDeclContext(LexicalDC);` that caused lookup problems? Repository: rG LLVM Github Monorepo CHANGES SINCE LAST ACTION https://reviews.llvm.org/D65935/new/ https://reviews.llvm.org/D65935 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[PATCH] D65935: [ASTImporter] Import ctor initializers after setting flags.
balazske created this revision. Herald added subscribers: cfe-commits, gamesh411, Szelethus, dkrupp. Herald added a reviewer: martong. Herald added a reviewer: a.sidorin. Herald added a reviewer: shafik. Herald added a project: clang. Code to import "ctor initializers" at import of functions is moved to be after the flags in the newly created function are imported. This fixes an error when the already created but incomplete (flags are not set) function declaration is accessed. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D65935 Files: clang/lib/AST/ASTImporter.cpp clang/test/Analysis/Inputs/ctu-other.cpp clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt clang/test/Analysis/ctu-main.cpp Index: clang/test/Analysis/ctu-main.cpp === --- clang/test/Analysis/ctu-main.cpp +++ clang/test/Analysis/ctu-main.cpp @@ -125,6 +125,8 @@ static const int Test; }; +extern int testImportOfDelegateConstructor(int); + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -158,4 +160,6 @@ // clang_analyzer_eval(extSCC.a == 7); // TODO clang_analyzer_eval(extU.a == 4); // expected-warning{{TRUE}} clang_analyzer_eval(TestAnonUnionUSR::Test == 5); // expected-warning{{TRUE}} + + clang_analyzer_eval(testImportOfDelegateConstructor(10) == 10); // expected-warning{{TRUE}} } Index: clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt === --- clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt +++ clang/test/Analysis/Inputs/ctu-other.cpp.externalDefMap.txt @@ -26,3 +26,4 @@ c:@extSCC ctu-other.cpp.ast c:@extU ctu-other.cpp.ast c:@S@TestAnonUnionUSR@Test ctu-other.cpp.ast +c:@F@testImportOfDelegateConstructor#I# ctu-other.cpp.ast Index: clang/test/Analysis/Inputs/ctu-other.cpp === --- clang/test/Analysis/Inputs/ctu-other.cpp +++ clang/test/Analysis/Inputs/ctu-other.cpp @@ -145,3 +145,14 @@ static const int Test; }; const int TestAnonUnionUSR::Test = 5; + +class TestDelegateConstructor { +public: + TestDelegateConstructor() : TestDelegateConstructor(2) {} + TestDelegateConstructor(int) {} +}; + +int testImportOfDelegateConstructor(int i) { + TestDelegateConstructor TDC; + return i; +} Index: clang/lib/AST/ASTImporter.cpp === --- clang/lib/AST/ASTImporter.cpp +++ clang/lib/AST/ASTImporter.cpp @@ -3265,23 +3265,6 @@ // decl and its redeclarations may be required. } - // Import Ctor initializers. - if (auto *FromConstructor = dyn_cast(D)) { -if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { - SmallVector CtorInitializers(NumInitializers); - // Import first, then allocate memory and copy if there was no error. - if (Error Err = ImportContainerChecked( - FromConstructor->inits(), CtorInitializers)) -return std::move(Err); - auto **Memory = - new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; - std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); - auto *ToCtor = cast(ToFunction); - ToCtor->setCtorInitializers(Memory); - ToCtor->setNumCtorInitializers(NumInitializers); -} - } - ToFunction->setQualifierInfo(ToQualifierLoc); ToFunction->setAccess(D->getAccess()); ToFunction->setLexicalDeclContext(LexicalDC); @@ -3307,6 +3290,23 @@ } } + // Import Ctor initializers. + if (auto *FromConstructor = dyn_cast(D)) { +if (unsigned NumInitializers = FromConstructor->getNumCtorInitializers()) { + SmallVector CtorInitializers(NumInitializers); + // Import first, then allocate memory and copy if there was no error. + if (Error Err = ImportContainerChecked( + FromConstructor->inits(), CtorInitializers)) +return std::move(Err); + auto **Memory = + new (Importer.getToContext()) CXXCtorInitializer *[NumInitializers]; + std::copy(CtorInitializers.begin(), CtorInitializers.end(), Memory); + auto *ToCtor = cast(ToFunction); + ToCtor->setCtorInitializers(Memory); + ToCtor->setNumCtorInitializers(NumInitializers); +} + } + if (usedDifferentExceptionSpec) { // Update FunctionProtoType::ExtProtoInfo. if (ExpectedType TyOrErr = import(D->getType())) Index: clang/test/Analysis/ctu-main.cpp === --- clang/test/Analysis/ctu-main.cpp +++ clang/test/Analysis/ctu-main.cpp @@ -125,6 +125,8 @@ static const int Test; }; +extern int testImportOfDelegateConstructor(int); + int main() { clang_analyzer_eval(f(3) == 2); // expected-warning{{TRUE}} clang_analyzer_eval(f(4) == 3); // expected-warning{{TRUE}} @@ -158,4 +160,6 @@