diff options
Diffstat (limited to 'clang/lib/Sema')
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 117 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExprMember.cpp | 28 | ||||
-rw-r--r-- | clang/lib/Sema/SemaInit.cpp | 13 | ||||
-rw-r--r-- | clang/lib/Sema/SemaType.cpp | 48 |
4 files changed, 199 insertions, 7 deletions
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 304a8079a55..ab8806302d0 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -4288,7 +4288,57 @@ Sema::ActOnArraySubscriptExpr(Scope *S, Expr *base, SourceLocation lbLoc, return CreateOverloadedArraySubscriptExpr(lbLoc, rbLoc, base, idx); } - return CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + ExprResult Res = CreateBuiltinArraySubscriptExpr(base, lbLoc, idx, rbLoc); + + if (!Res.isInvalid() && isa<ArraySubscriptExpr>(Res.get())) + CheckSubscriptAccessOfNoDeref(cast<ArraySubscriptExpr>(Res.get())); + + return Res; +} + +void Sema::CheckAddressOfNoDeref(const Expr *E) { + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + const Expr *StrippedExpr = E->IgnoreParenImpCasts(); + + // For expressions like `&(*s).b`, the base is recorded and what should be + // checked. + const MemberExpr *Member = nullptr; + while ((Member = dyn_cast<MemberExpr>(StrippedExpr)) && !Member->isArrow()) + StrippedExpr = Member->getBase()->IgnoreParenImpCasts(); + + LastRecord.PossibleDerefs.erase(StrippedExpr); +} + +void Sema::CheckSubscriptAccessOfNoDeref(const ArraySubscriptExpr *E) { + QualType ResultTy = E->getType(); + ExpressionEvaluationContextRecord &LastRecord = ExprEvalContexts.back(); + + // Bail if the element is an array since it is not memory access. + if (isa<ArrayType>(ResultTy)) + return; + + if (ResultTy->hasAttr(attr::NoDeref)) { + LastRecord.PossibleDerefs.insert(E); + return; + } + + // Check if the base type is a pointer to a member access of a struct + // marked with noderef. + const Expr *Base = E->getBase(); + QualType BaseTy = Base->getType(); + if (!(isa<ArrayType>(BaseTy) || isa<PointerType>(BaseTy))) + // Not a pointer access + return; + + const MemberExpr *Member = nullptr; + while ((Member = dyn_cast<MemberExpr>(Base->IgnoreParenCasts())) && + Member->isArrow()) + Base = Member->getBase(); + + if (const auto *Ptr = dyn_cast<PointerType>(Base->getType())) { + if (Ptr->getPointeeType()->hasAttr(attr::NoDeref)) + LastRecord.PossibleDerefs.insert(E); + } } ExprResult Sema::ActOnOMPArraySectionExpr(Expr *Base, SourceLocation LBLoc, @@ -8157,6 +8207,17 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, ExprResult LocalRHS = CallerRHS; ExprResult &RHS = ConvertRHS ? CallerRHS : LocalRHS; + if (const auto *LHSPtrType = LHSType->getAs<PointerType>()) { + if (const auto *RHSPtrType = RHS.get()->getType()->getAs<PointerType>()) { + if (RHSPtrType->getPointeeType()->hasAttr(attr::NoDeref) && + !LHSPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { + Diag(RHS.get()->getExprLoc(), + diag::warn_noderef_to_dereferenceable_pointer) + << RHS.get()->getSourceRange(); + } + } + } + if (getLangOpts().CPlusPlus) { if (!LHSType->isRecordType() && !LHSType->isAtomicType()) { // C++ 5.17p3: If the left operand is not of class type, the @@ -8277,6 +8338,7 @@ Sema::CheckSingleAssignmentConstraints(QualType LHSType, ExprResult &CallerRHS, if (ConvertRHS) RHS = ImpCastExprToType(E, Ty, Kind); } + return result; } @@ -12825,6 +12887,7 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, break; case UO_AddrOf: resultType = CheckAddressOfOperand(Input, OpLoc); + CheckAddressOfNoDeref(InputExpr); RecordModifiableNonNullParam(*this, InputExpr); break; case UO_Deref: { @@ -12989,6 +13052,11 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc, auto *UO = new (Context) UnaryOperator(Input.get(), Opc, resultType, VK, OK, OpLoc, CanOverflow); + + if (Opc == UO_Deref && UO->getType()->hasAttr(attr::NoDeref) && + !isa<ArrayType>(UO->getType().getDesugaredType(Context))) + ExprEvalContexts.back().PossibleDerefs.insert(UO); + // Convert the result back to a half vector. if (ConvertHalfVec) return convertVector(UO, Context.HalfTy, *this); @@ -14355,6 +14423,51 @@ Sema::PushExpressionEvaluationContext( PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext); } +namespace { + +const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) { + PossibleDeref = PossibleDeref->IgnoreParenImpCasts(); + if (const auto *E = dyn_cast<UnaryOperator>(PossibleDeref)) { + if (E->getOpcode() == UO_Deref) + return CheckPossibleDeref(S, E->getSubExpr()); + } else if (const auto *E = dyn_cast<ArraySubscriptExpr>(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto *E = dyn_cast<MemberExpr>(PossibleDeref)) { + return CheckPossibleDeref(S, E->getBase()); + } else if (const auto E = dyn_cast<DeclRefExpr>(PossibleDeref)) { + QualType Inner; + QualType Ty = E->getType(); + if (const auto *Ptr = Ty->getAs<PointerType>()) + Inner = Ptr->getPointeeType(); + else if (const auto *Arr = S.Context.getAsArrayType(Ty)) + Inner = Arr->getElementType(); + else + return nullptr; + + if (Inner->hasAttr(attr::NoDeref)) + return E; + } + return nullptr; +} + +} // namespace + +void Sema::WarnOnPendingNoDerefs(ExpressionEvaluationContextRecord &Rec) { + for (const Expr *E : Rec.PossibleDerefs) { + const DeclRefExpr *DeclRef = CheckPossibleDeref(*this, E); + if (DeclRef) { + const ValueDecl *Decl = DeclRef->getDecl(); + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type) + << Decl->getName() << E->getSourceRange(); + Diag(Decl->getLocation(), diag::note_previous_decl) << Decl->getName(); + } else { + Diag(E->getExprLoc(), diag::warn_dereference_of_noderef_type_no_decl) + << E->getSourceRange(); + } + } + Rec.PossibleDerefs.clear(); +} + void Sema::PopExpressionEvaluationContext() { ExpressionEvaluationContextRecord& Rec = ExprEvalContexts.back(); unsigned NumTypos = Rec.NumTypos; @@ -14394,6 +14507,8 @@ void Sema::PopExpressionEvaluationContext() { } } + WarnOnPendingNoDerefs(Rec); + // When are coming out of an unevaluated context, clear out any // temporaries that we may have created as part of the evaluation of // the expression in that context: they aren't relevant because they diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 040b2e36eb9..b2b21ba9eef 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1708,9 +1708,31 @@ ExprResult Sema::ActOnMemberAccessExpr(Scope *S, Expr *Base, } ActOnMemberAccessExtraArgs ExtraArgs = {S, Id, ObjCImpDecl}; - return BuildMemberReferenceExpr(Base, Base->getType(), OpLoc, IsArrow, SS, - TemplateKWLoc, FirstQualifierInScope, - NameInfo, TemplateArgs, S, &ExtraArgs); + ExprResult Res = BuildMemberReferenceExpr( + Base, Base->getType(), OpLoc, IsArrow, SS, TemplateKWLoc, + FirstQualifierInScope, NameInfo, TemplateArgs, S, &ExtraArgs); + + if (!Res.isInvalid() && isa<MemberExpr>(Res.get())) + CheckMemberAccessOfNoDeref(cast<MemberExpr>(Res.get())); + + return Res; +} + +void Sema::CheckMemberAccessOfNoDeref(const MemberExpr *E) { + QualType ResultTy = E->getType(); + + // Do not warn on member accesses to arrays since this returns an array + // lvalue and does not actually dereference memory. + if (isa<ArrayType>(ResultTy)) + return; + + if (E->isArrow()) { + if (const auto *Ptr = dyn_cast<PointerType>( + E->getBase()->getType().getDesugaredType(Context))) { + if (Ptr->getPointeeType()->hasAttr(attr::NoDeref)) + ExprEvalContexts.back().PossibleDerefs.insert(E); + } + } } ExprResult diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 12946060084..b5c387b169a 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -7687,6 +7687,18 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_ConversionSequence: case SK_ConversionSequenceNoNarrowing: { + if (const auto *FromPtrType = + CurInit.get()->getType()->getAs<PointerType>()) { + if (const auto *ToPtrType = Step->Type->getAs<PointerType>()) { + if (FromPtrType->getPointeeType()->hasAttr(attr::NoDeref) && + !ToPtrType->getPointeeType()->hasAttr(attr::NoDeref)) { + S.Diag(CurInit.get()->getExprLoc(), + diag::warn_noderef_to_dereferenceable_pointer) + << CurInit.get()->getSourceRange(); + } + } + } + Sema::CheckedConversionKind CCK = Kind.isCStyleCast()? Sema::CCK_CStyleCast : Kind.isFunctionalCast()? Sema::CCK_FunctionalCast @@ -7853,6 +7865,7 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_CAssignment: { QualType SourceType = CurInit.get()->getType(); + // Save off the initial CurInit in case we need to emit a diagnostic ExprResult InitialCurInit = CurInit; ExprResult Result = CurInit; diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 3d0e70ac7bd..20d9bb83585 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -183,11 +183,15 @@ namespace { SmallVector<TypeAttrPair, 8> AttrsForTypes; bool AttrsForTypesSorted = true; + /// Flag to indicate we parsed a noderef attribute. This is used for + /// validating that noderef was used on a pointer or array. + bool parsedNoDeref; + public: TypeProcessingState(Sema &sema, Declarator &declarator) - : sema(sema), declarator(declarator), - chunkIndex(declarator.getNumTypeObjects()), - trivial(true), hasSavedAttrs(false) {} + : sema(sema), declarator(declarator), + chunkIndex(declarator.getNumTypeObjects()), trivial(true), + hasSavedAttrs(false), parsedNoDeref(false) {} Sema &getSema() const { return sema; @@ -278,6 +282,10 @@ namespace { llvm_unreachable("no Attr* for AttributedType*"); } + void setParsedNoDeref(bool parsed) { parsedNoDeref = parsed; } + + bool didParseNoDeref() const { return parsedNoDeref; } + ~TypeProcessingState() { if (trivial) return; @@ -3887,6 +3895,11 @@ static bool hasOuterPointerLikeChunk(const Declarator &D, unsigned endIndex) { return false; } +static bool IsNoDerefableChunk(DeclaratorChunk Chunk) { + return (Chunk.Kind == DeclaratorChunk::Pointer || + Chunk.Kind == DeclaratorChunk::Array); +} + template<typename AttrT> static AttrT *createSimpleAttr(ASTContext &Ctx, ParsedAttr &Attr) { Attr.setUsedAsTypeAttr(); @@ -4280,6 +4293,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, } } + bool ExpectNoDerefChunk = + state.getCurrentAttributes().hasAttribute(ParsedAttr::AT_NoDeref); + // Walk the DeclTypeInfo, building the recursive type as we go. // DeclTypeInfos are ordered from the identifier out, which is // opposite of what we want :). @@ -4889,8 +4905,22 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state, // See if there are any attributes on this declarator chunk. processTypeAttrs(state, T, TAL_DeclChunk, DeclType.getAttrs()); + + if (DeclType.Kind != DeclaratorChunk::Paren) { + if (ExpectNoDerefChunk) { + if (!IsNoDerefableChunk(DeclType)) + S.Diag(DeclType.Loc, diag::warn_noderef_on_non_pointer_or_array); + ExpectNoDerefChunk = false; + } + + ExpectNoDerefChunk = state.didParseNoDeref(); + } } + if (ExpectNoDerefChunk) + S.Diag(state.getDeclarator().getBeginLoc(), + diag::warn_noderef_on_non_pointer_or_array); + // GNU warning -Wstrict-prototypes // Warn if a function declaration is without a prototype. // This warning is issued for all kinds of unprototyped function @@ -7271,6 +7301,9 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, // sure we visit every element once. Copy the attributes list, and iterate // over that. ParsedAttributesView AttrsCopy{attrs}; + + state.setParsedNoDeref(false); + for (ParsedAttr &attr : AttrsCopy) { // Skip attributes that were marked to be invalid. @@ -7368,6 +7401,15 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, HandleLifetimeBoundAttr(state, type, attr); break; + case ParsedAttr::AT_NoDeref: { + ASTContext &Ctx = state.getSema().Context; + type = state.getAttributedType(createSimpleAttr<NoDerefAttr>(Ctx, attr), + type, type); + attr.setUsedAsTypeAttr(); + state.setParsedNoDeref(true); + break; + } + MS_TYPE_ATTRS_CASELIST: if (!handleMSPointerTypeQualifierAttr(state, attr, type)) attr.setUsedAsTypeAttr(); |