diff options
Diffstat (limited to 'lib/Sema/SemaDecl.cpp')
-rw-r--r-- | lib/Sema/SemaDecl.cpp | 240 |
1 files changed, 222 insertions, 18 deletions
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index c7545cf532..4476211bc0 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -1928,6 +1928,7 @@ bool Sema::MergeCompatibleFunctionDecls(FunctionDecl *New, FunctionDecl *Old) { return false; } + void Sema::mergeObjCMethodDecls(ObjCMethodDecl *newMethod, const ObjCMethodDecl *oldMethod) { // Merge the attributes. @@ -3473,6 +3474,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_lifetime) + << var->getType(); + return true; + } + } + + return false; +} + NamedDecl* Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, QualType R, TypeSourceInfo *TInfo, @@ -3631,6 +3676,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. @@ -5229,6 +5279,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()) @@ -5471,7 +5525,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); @@ -5740,6 +5797,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; @@ -6008,7 +6082,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) @@ -6019,7 +6093,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) @@ -6033,6 +6107,28 @@ 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_lifetime) + << 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, StorageClass, StorageClassAsWritten, @@ -6369,7 +6465,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)) { @@ -6384,15 +6480,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) @@ -6403,8 +6501,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; } @@ -7736,6 +7836,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); @@ -7769,6 +7874,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 lifetime")); + return false; + } + } + Diag(FD->getLocation(), diag::err_illegal_union_or_anon_struct_member) << (int)FD->getParent()->isUnion() << FD->getDeclName() << member; DiagnoseNontrivial(RT, member); @@ -7921,6 +8041,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_lifetime) + << QT << EltTy.getObjCLifetime(); + return; + } + } } assert(0 && "found no explanation for non-trivial member"); @@ -8030,6 +8165,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. @@ -8101,6 +8240,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]); @@ -8166,7 +8306,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(); @@ -8213,17 +8353,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 lifetime")); + } + } 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()) @@ -8242,6 +8410,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()) |