diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 7dea6dc56f36..5db8e20e7e48 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -898,17 +898,18 @@ public: return getCanonicalType(T1) == getCanonicalType(T2); } - /// \brief Returns this type as a completely-unqualified array type, capturing - /// the qualifiers in Quals. This only operates on canonical types in order - /// to ensure the ArrayType doesn't itself have qualifiers. + /// \brief Returns this type as a completely-unqualified array type, + /// capturing the qualifiers in Quals. This will remove the minimal amount of + /// sugaring from the types, similar to the behavior of + /// QualType::getUnqualifiedType(). /// - /// \param T is the canonicalized QualType, which may be an ArrayType + /// \param T is the qualified type, which may be an ArrayType /// /// \param Quals will receive the full set of qualifiers that were - /// applied to the element type of the array. + /// applied to the array. /// /// \returns if this is an array type, the completely unqualified array type - /// that corresponds to it. Otherwise, returns this->getUnqualifiedType(). + /// that corresponds to it. Otherwise, returns T.getUnqualifiedType(). QualType getUnqualifiedArrayType(QualType T, Qualifiers &Quals); /// \brief Determine whether the given types are equivalent after diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index e729b1b58d55..76ec852cb89e 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -2374,13 +2374,11 @@ CanQualType ASTContext::getCanonicalType(QualType T) { QualType ASTContext::getUnqualifiedArrayType(QualType T, Qualifiers &Quals) { - assert(T.isCanonical() && "Only operates on canonical types"); + Quals = T.getQualifiers(); if (!isa(T)) { - Quals = T.getLocalQualifiers(); - return T.getLocalUnqualifiedType(); + return T.getUnqualifiedType(); } - assert(!T.hasQualifiers() && "canonical array type has qualifiers!"); const ArrayType *AT = cast(T); QualType Elt = AT->getElementType(); QualType UnqualElt = getUnqualifiedArrayType(Elt, Quals); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 0079265a38b6..1970f56e284b 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2227,9 +2227,11 @@ static void TryReferenceInitialization(Sema &S, QualType DestType = Entity.getType(); QualType cv1T1 = DestType->getAs()->getPointeeType(); - QualType T1 = cv1T1.getUnqualifiedType(); + Qualifiers T1Quals; + QualType T1 = S.Context.getUnqualifiedArrayType(cv1T1, T1Quals); QualType cv2T2 = Initializer->getType(); - QualType T2 = cv2T2.getUnqualifiedType(); + Qualifiers T2Quals; + QualType T2 = S.Context.getUnqualifiedArrayType(cv2T2, T2Quals); SourceLocation DeclLoc = Initializer->getLocStart(); // If the initializer is the address of an overloaded function, try @@ -2279,9 +2281,9 @@ static void TryReferenceInitialization(Sema &S, // can occur. This property will be checked by PerformInitialization. if (DerivedToBase) Sequence.AddDerivedToBaseCastStep( - S.Context.getQualifiedType(T1, cv2T2.getQualifiers()), + S.Context.getQualifiedType(T1, T2Quals), /*isLValue=*/true); - if (cv1T1.getQualifiers() != cv2T2.getQualifiers()) + if (T1Quals != T2Quals) Sequence.AddQualificationConversionStep(cv1T1, /*IsLValue=*/true); Sequence.AddReferenceBindingStep(cv1T1, /*bindingTemporary=*/false); return; @@ -2312,7 +2314,7 @@ static void TryReferenceInitialization(Sema &S, // non-volatile const type (i.e., cv1 shall be const), or the reference // shall be an rvalue reference and the initializer expression shall // be an rvalue. - if (!((isLValueRef && cv1T1.getCVRQualifiers() == Qualifiers::Const) || + if (!((isLValueRef && T1Quals.hasConst()) || (isRValueRef && InitLvalue != Expr::LV_Valid))) { if (ConvOvlResult && !Sequence.getFailedCandidateSet().empty()) Sequence.SetOverloadFailure( @@ -2339,9 +2341,9 @@ static void TryReferenceInitialization(Sema &S, RefRelationship >= Sema::Ref_Compatible_With_Added_Qualification) { if (DerivedToBase) Sequence.AddDerivedToBaseCastStep( - S.Context.getQualifiedType(T1, cv2T2.getQualifiers()), + S.Context.getQualifiedType(T1, T2Quals), /*isLValue=*/false); - if (cv1T1.getQualifiers() != cv2T2.getQualifiers()) + if (T1Quals != T2Quals) Sequence.AddQualificationConversionStep(cv1T1, /*IsLValue=*/false); Sequence.AddReferenceBindingStep(cv1T1, /*bindingTemporary=*/true); return; @@ -2406,8 +2408,10 @@ static void TryReferenceInitialization(Sema &S, // [...] If T1 is reference-related to T2, cv1 must be the // same cv-qualification as, or greater cv-qualification // than, cv2; otherwise, the program is ill-formed. + unsigned T1CVRQuals = T1Quals.getCVRQualifiers(); + unsigned T2CVRQuals = T2Quals.getCVRQualifiers(); if (RefRelationship == Sema::Ref_Related && - !cv1T1.isAtLeastAsQualifiedAs(cv2T2)) { + (T1CVRQuals | T2CVRQuals) != T1CVRQuals) { Sequence.SetFailed(InitializationSequence::FK_ReferenceInitDropsQualifiers); return; } diff --git a/clang/test/CodeGenCXX/reference-init.cpp b/clang/test/CodeGenCXX/reference-init.cpp index 1bfb28a66a03..6c2c6a301681 100644 --- a/clang/test/CodeGenCXX/reference-init.cpp +++ b/clang/test/CodeGenCXX/reference-init.cpp @@ -7,3 +7,10 @@ struct nsXPTParamInfo { void a(XPTParamDescriptor *params) { const nsXPTParamInfo& paramInfo = params[0]; } + +// CodeGen of reference initialized const arrays. +namespace PR5911 { + template int f(const T (&a)[N]) { return N; } + int iarr[] = { 1 }; + int test() { return f(iarr); } +}