This patch reapplies #90500, addressing a bug which caused binary operators with dependent operands to be incorrectly rebuilt by `TreeTransform`.
This commit is contained in:
committed by
GitHub
parent
f60c699d37
commit
1595988ee6
@@ -56,6 +56,9 @@ C++ Specific Potentially Breaking Changes
|
||||
|
||||
- Clang now rejects pointer to member from parenthesized expression in unevaluated context such as ``decltype(&(foo::bar))``. (#GH40906).
|
||||
|
||||
- Clang now performs semantic analysis for unary operators with dependent operands
|
||||
that are known to be of non-class non-enumeration type prior to instantiation.
|
||||
|
||||
ABI Changes in This Version
|
||||
---------------------------
|
||||
- Fixed Microsoft name mangling of implicitly defined variables used for thread
|
||||
|
||||
@@ -8044,7 +8044,10 @@ inline bool Type::isUndeducedType() const {
|
||||
/// Determines whether this is a type for which one can define
|
||||
/// an overloaded operator.
|
||||
inline bool Type::isOverloadableType() const {
|
||||
return isDependentType() || isRecordType() || isEnumeralType();
|
||||
if (!CanonicalType->isDependentType())
|
||||
return isRecordType() || isEnumeralType();
|
||||
return !isArrayType() && !isFunctionType() && !isAnyPointerType() &&
|
||||
!isMemberPointerType();
|
||||
}
|
||||
|
||||
/// Determines whether this type is written as a typedef-name.
|
||||
|
||||
@@ -672,12 +672,12 @@ ExprResult Sema::DefaultLvalueConversion(Expr *E) {
|
||||
|
||||
// We don't want to throw lvalue-to-rvalue casts on top of
|
||||
// expressions of certain types in C++.
|
||||
if (getLangOpts().CPlusPlus &&
|
||||
(E->getType() == Context.OverloadTy ||
|
||||
// FIXME: This is a hack! We want the lvalue-to-rvalue conversion applied
|
||||
// to pointer types even if the pointee type is dependent.
|
||||
(T->isDependentType() && !T->isPointerType()) || T->isRecordType()))
|
||||
return E;
|
||||
if (getLangOpts().CPlusPlus) {
|
||||
if (T == Context.OverloadTy || T->isRecordType() ||
|
||||
(T->isDependentType() && !T->isAnyPointerType() &&
|
||||
!T->isMemberPointerType()))
|
||||
return E;
|
||||
}
|
||||
|
||||
// The C standard is actually really unclear on this point, and
|
||||
// DR106 tells us what the result should be but not why. It's
|
||||
@@ -10827,7 +10827,7 @@ static bool checkArithmeticIncompletePointerType(Sema &S, SourceLocation Loc,
|
||||
if (const AtomicType *ResAtomicType = ResType->getAs<AtomicType>())
|
||||
ResType = ResAtomicType->getValueType();
|
||||
|
||||
assert(ResType->isAnyPointerType() && !ResType->isDependentType());
|
||||
assert(ResType->isAnyPointerType());
|
||||
QualType PointeeTy = ResType->getPointeeType();
|
||||
return S.RequireCompleteSizedType(
|
||||
Loc, PointeeTy,
|
||||
@@ -13957,9 +13957,6 @@ static QualType CheckIncrementDecrementOperand(Sema &S, Expr *Op,
|
||||
ExprObjectKind &OK,
|
||||
SourceLocation OpLoc, bool IsInc,
|
||||
bool IsPrefix) {
|
||||
if (Op->isTypeDependent())
|
||||
return S.Context.DependentTy;
|
||||
|
||||
QualType ResType = Op->getType();
|
||||
// Atomic types can be used for increment / decrement where the non-atomic
|
||||
// versions can, so ignore the _Atomic() specifier for the purpose of
|
||||
@@ -14410,9 +14407,6 @@ static void RecordModifiableNonNullParam(Sema &S, const Expr *Exp) {
|
||||
static QualType CheckIndirectionOperand(Sema &S, Expr *Op, ExprValueKind &VK,
|
||||
SourceLocation OpLoc,
|
||||
bool IsAfterAmp = false) {
|
||||
if (Op->isTypeDependent())
|
||||
return S.Context.DependentTy;
|
||||
|
||||
ExprResult ConvResult = S.UsualUnaryConversions(Op);
|
||||
if (ConvResult.isInvalid())
|
||||
return QualType();
|
||||
@@ -15368,14 +15362,10 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc,
|
||||
}
|
||||
|
||||
if (getLangOpts().CPlusPlus) {
|
||||
// If either expression is type-dependent, always build an
|
||||
// overloaded op.
|
||||
if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent())
|
||||
return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr);
|
||||
|
||||
// Otherwise, build an overloaded op if either expression has an
|
||||
// overloadable type.
|
||||
if (LHSExpr->getType()->isOverloadableType() ||
|
||||
// Otherwise, build an overloaded op if either expression is type-dependent
|
||||
// or has an overloadable type.
|
||||
if (LHSExpr->isTypeDependent() || RHSExpr->isTypeDependent() ||
|
||||
LHSExpr->getType()->isOverloadableType() ||
|
||||
RHSExpr->getType()->isOverloadableType())
|
||||
return BuildOverloadedBinOp(*this, S, OpLoc, Opc, LHSExpr, RHSExpr);
|
||||
}
|
||||
@@ -15466,190 +15456,191 @@ ExprResult Sema::CreateBuiltinUnaryOp(SourceLocation OpLoc,
|
||||
return ExprError(Diag(OpLoc, diag::err_hlsl_operator_unsupported) << 1);
|
||||
}
|
||||
|
||||
switch (Opc) {
|
||||
case UO_PreInc:
|
||||
case UO_PreDec:
|
||||
case UO_PostInc:
|
||||
case UO_PostDec:
|
||||
resultType =
|
||||
CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc,
|
||||
Opc == UO_PreInc || Opc == UO_PostInc,
|
||||
Opc == UO_PreInc || Opc == UO_PreDec);
|
||||
CanOverflow = isOverflowingIntegerType(Context, resultType);
|
||||
break;
|
||||
case UO_AddrOf:
|
||||
resultType = CheckAddressOfOperand(Input, OpLoc);
|
||||
CheckAddressOfNoDeref(InputExpr);
|
||||
RecordModifiableNonNullParam(*this, InputExpr);
|
||||
break;
|
||||
case UO_Deref: {
|
||||
Input = DefaultFunctionArrayLvalueConversion(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
resultType =
|
||||
CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp);
|
||||
break;
|
||||
}
|
||||
case UO_Plus:
|
||||
case UO_Minus:
|
||||
CanOverflow = Opc == UO_Minus &&
|
||||
isOverflowingIntegerType(Context, Input.get()->getType());
|
||||
Input = UsualUnaryConversions(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
// Unary plus and minus require promoting an operand of half vector to a
|
||||
// float vector and truncating the result back to a half vector. For now, we
|
||||
// do this only when HalfArgsAndReturns is set (that is, when the target is
|
||||
// arm or arm64).
|
||||
ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get());
|
||||
if (InputExpr->isTypeDependent() &&
|
||||
InputExpr->getType()->isSpecificBuiltinType(BuiltinType::Dependent)) {
|
||||
resultType = Context.DependentTy;
|
||||
} else {
|
||||
switch (Opc) {
|
||||
case UO_PreInc:
|
||||
case UO_PreDec:
|
||||
case UO_PostInc:
|
||||
case UO_PostDec:
|
||||
resultType =
|
||||
CheckIncrementDecrementOperand(*this, Input.get(), VK, OK, OpLoc,
|
||||
Opc == UO_PreInc || Opc == UO_PostInc,
|
||||
Opc == UO_PreInc || Opc == UO_PreDec);
|
||||
CanOverflow = isOverflowingIntegerType(Context, resultType);
|
||||
break;
|
||||
case UO_AddrOf:
|
||||
resultType = CheckAddressOfOperand(Input, OpLoc);
|
||||
CheckAddressOfNoDeref(InputExpr);
|
||||
RecordModifiableNonNullParam(*this, InputExpr);
|
||||
break;
|
||||
case UO_Deref: {
|
||||
Input = DefaultFunctionArrayLvalueConversion(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
resultType =
|
||||
CheckIndirectionOperand(*this, Input.get(), VK, OpLoc, IsAfterAmp);
|
||||
break;
|
||||
}
|
||||
case UO_Plus:
|
||||
case UO_Minus:
|
||||
CanOverflow = Opc == UO_Minus &&
|
||||
isOverflowingIntegerType(Context, Input.get()->getType());
|
||||
Input = UsualUnaryConversions(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
// Unary plus and minus require promoting an operand of half vector to a
|
||||
// float vector and truncating the result back to a half vector. For now,
|
||||
// we do this only when HalfArgsAndReturns is set (that is, when the
|
||||
// target is arm or arm64).
|
||||
ConvertHalfVec = needsConversionOfHalfVec(true, Context, Input.get());
|
||||
|
||||
// If the operand is a half vector, promote it to a float vector.
|
||||
if (ConvertHalfVec)
|
||||
Input = convertVector(Input.get(), Context.FloatTy, *this);
|
||||
resultType = Input.get()->getType();
|
||||
if (resultType->isDependentType())
|
||||
break;
|
||||
if (resultType->isArithmeticType()) // C99 6.5.3.3p1
|
||||
break;
|
||||
else if (resultType->isVectorType() &&
|
||||
// The z vector extensions don't allow + or - with bool vectors.
|
||||
(!Context.getLangOpts().ZVector ||
|
||||
resultType->castAs<VectorType>()->getVectorKind() !=
|
||||
VectorKind::AltiVecBool))
|
||||
break;
|
||||
else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and -
|
||||
break;
|
||||
else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6
|
||||
Opc == UO_Plus && resultType->isPointerType())
|
||||
break;
|
||||
// If the operand is a half vector, promote it to a float vector.
|
||||
if (ConvertHalfVec)
|
||||
Input = convertVector(Input.get(), Context.FloatTy, *this);
|
||||
resultType = Input.get()->getType();
|
||||
if (resultType->isArithmeticType()) // C99 6.5.3.3p1
|
||||
break;
|
||||
else if (resultType->isVectorType() &&
|
||||
// The z vector extensions don't allow + or - with bool vectors.
|
||||
(!Context.getLangOpts().ZVector ||
|
||||
resultType->castAs<VectorType>()->getVectorKind() !=
|
||||
VectorKind::AltiVecBool))
|
||||
break;
|
||||
else if (resultType->isSveVLSBuiltinType()) // SVE vectors allow + and -
|
||||
break;
|
||||
else if (getLangOpts().CPlusPlus && // C++ [expr.unary.op]p6
|
||||
Opc == UO_Plus && resultType->isPointerType())
|
||||
break;
|
||||
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
|
||||
case UO_Not: // bitwise complement
|
||||
Input = UsualUnaryConversions(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
resultType = Input.get()->getType();
|
||||
if (resultType->isDependentType())
|
||||
break;
|
||||
// C99 6.5.3.3p1. We allow complex int and float as a GCC extension.
|
||||
if (resultType->isComplexType() || resultType->isComplexIntegerType())
|
||||
// C99 does not support '~' for complex conjugation.
|
||||
Diag(OpLoc, diag::ext_integer_complement_complex)
|
||||
<< resultType << Input.get()->getSourceRange();
|
||||
else if (resultType->hasIntegerRepresentation())
|
||||
break;
|
||||
else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) {
|
||||
// OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate
|
||||
// on vector float types.
|
||||
QualType T = resultType->castAs<ExtVectorType>()->getElementType();
|
||||
if (!T->isIntegerType())
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
} else {
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
break;
|
||||
|
||||
case UO_LNot: // logical negation
|
||||
// Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5).
|
||||
Input = DefaultFunctionArrayLvalueConversion(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
resultType = Input.get()->getType();
|
||||
|
||||
// Though we still have to promote half FP to float...
|
||||
if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) {
|
||||
Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast)
|
||||
.get();
|
||||
resultType = Context.FloatTy;
|
||||
}
|
||||
|
||||
// WebAsembly tables can't be used in unary expressions.
|
||||
if (resultType->isPointerType() &&
|
||||
resultType->getPointeeType().isWebAssemblyReferenceType()) {
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
|
||||
if (resultType->isDependentType())
|
||||
break;
|
||||
if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) {
|
||||
// C99 6.5.3.3p1: ok, fallthrough;
|
||||
if (Context.getLangOpts().CPlusPlus) {
|
||||
// C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
|
||||
// operand contextually converted to bool.
|
||||
Input = ImpCastExprToType(Input.get(), Context.BoolTy,
|
||||
ScalarTypeToBooleanCastKind(resultType));
|
||||
} else if (Context.getLangOpts().OpenCL &&
|
||||
Context.getLangOpts().OpenCLVersion < 120) {
|
||||
// OpenCL v1.1 6.3.h: The logical operator not (!) does not
|
||||
// operate on scalar float types.
|
||||
if (!resultType->isIntegerType() && !resultType->isPointerType())
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
} else if (resultType->isExtVectorType()) {
|
||||
if (Context.getLangOpts().OpenCL &&
|
||||
Context.getLangOpts().getOpenCLCompatibleVersion() < 120) {
|
||||
// OpenCL v1.1 6.3.h: The logical operator not (!) does not
|
||||
// operate on vector float types.
|
||||
case UO_Not: // bitwise complement
|
||||
Input = UsualUnaryConversions(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
resultType = Input.get()->getType();
|
||||
// C99 6.5.3.3p1. We allow complex int and float as a GCC extension.
|
||||
if (resultType->isComplexType() || resultType->isComplexIntegerType())
|
||||
// C99 does not support '~' for complex conjugation.
|
||||
Diag(OpLoc, diag::ext_integer_complement_complex)
|
||||
<< resultType << Input.get()->getSourceRange();
|
||||
else if (resultType->hasIntegerRepresentation())
|
||||
break;
|
||||
else if (resultType->isExtVectorType() && Context.getLangOpts().OpenCL) {
|
||||
// OpenCL v1.1 s6.3.f: The bitwise operator not (~) does not operate
|
||||
// on vector float types.
|
||||
QualType T = resultType->castAs<ExtVectorType>()->getElementType();
|
||||
if (!T->isIntegerType())
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
// Vector logical not returns the signed variant of the operand type.
|
||||
resultType = GetSignedVectorType(resultType);
|
||||
break;
|
||||
} else if (Context.getLangOpts().CPlusPlus && resultType->isVectorType()) {
|
||||
const VectorType *VTy = resultType->castAs<VectorType>();
|
||||
if (VTy->getVectorKind() != VectorKind::Generic)
|
||||
} else {
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
|
||||
// Vector logical not returns the signed variant of the operand type.
|
||||
resultType = GetSignedVectorType(resultType);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
|
||||
// LNot always has type int. C99 6.5.3.3p5.
|
||||
// In C++, it's bool. C++ 5.3.1p8
|
||||
resultType = Context.getLogicalOperationType();
|
||||
break;
|
||||
case UO_Real:
|
||||
case UO_Imag:
|
||||
resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real);
|
||||
// _Real maps ordinary l-values into ordinary l-values. _Imag maps ordinary
|
||||
// complex l-values to ordinary l-values and all other values to r-values.
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) {
|
||||
if (Input.get()->isGLValue() &&
|
||||
Input.get()->getObjectKind() == OK_Ordinary)
|
||||
VK = Input.get()->getValueKind();
|
||||
} else if (!getLangOpts().CPlusPlus) {
|
||||
// In C, a volatile scalar is read by __imag. In C++, it is not.
|
||||
Input = DefaultLvalueConversion(Input.get());
|
||||
case UO_LNot: // logical negation
|
||||
// Unlike +/-/~, integer promotions aren't done here (C99 6.5.3.3p5).
|
||||
Input = DefaultFunctionArrayLvalueConversion(Input.get());
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
resultType = Input.get()->getType();
|
||||
|
||||
// Though we still have to promote half FP to float...
|
||||
if (resultType->isHalfType() && !Context.getLangOpts().NativeHalfType) {
|
||||
Input = ImpCastExprToType(Input.get(), Context.FloatTy, CK_FloatingCast)
|
||||
.get();
|
||||
resultType = Context.FloatTy;
|
||||
}
|
||||
|
||||
// WebAsembly tables can't be used in unary expressions.
|
||||
if (resultType->isPointerType() &&
|
||||
resultType->getPointeeType().isWebAssemblyReferenceType()) {
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
|
||||
if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) {
|
||||
// C99 6.5.3.3p1: ok, fallthrough;
|
||||
if (Context.getLangOpts().CPlusPlus) {
|
||||
// C++03 [expr.unary.op]p8, C++0x [expr.unary.op]p9:
|
||||
// operand contextually converted to bool.
|
||||
Input = ImpCastExprToType(Input.get(), Context.BoolTy,
|
||||
ScalarTypeToBooleanCastKind(resultType));
|
||||
} else if (Context.getLangOpts().OpenCL &&
|
||||
Context.getLangOpts().OpenCLVersion < 120) {
|
||||
// OpenCL v1.1 6.3.h: The logical operator not (!) does not
|
||||
// operate on scalar float types.
|
||||
if (!resultType->isIntegerType() && !resultType->isPointerType())
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
} else if (resultType->isExtVectorType()) {
|
||||
if (Context.getLangOpts().OpenCL &&
|
||||
Context.getLangOpts().getOpenCLCompatibleVersion() < 120) {
|
||||
// OpenCL v1.1 6.3.h: The logical operator not (!) does not
|
||||
// operate on vector float types.
|
||||
QualType T = resultType->castAs<ExtVectorType>()->getElementType();
|
||||
if (!T->isIntegerType())
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
// Vector logical not returns the signed variant of the operand type.
|
||||
resultType = GetSignedVectorType(resultType);
|
||||
break;
|
||||
} else if (Context.getLangOpts().CPlusPlus &&
|
||||
resultType->isVectorType()) {
|
||||
const VectorType *VTy = resultType->castAs<VectorType>();
|
||||
if (VTy->getVectorKind() != VectorKind::Generic)
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
|
||||
// Vector logical not returns the signed variant of the operand type.
|
||||
resultType = GetSignedVectorType(resultType);
|
||||
break;
|
||||
} else {
|
||||
return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr)
|
||||
<< resultType << Input.get()->getSourceRange());
|
||||
}
|
||||
|
||||
// LNot always has type int. C99 6.5.3.3p5.
|
||||
// In C++, it's bool. C++ 5.3.1p8
|
||||
resultType = Context.getLogicalOperationType();
|
||||
break;
|
||||
case UO_Real:
|
||||
case UO_Imag:
|
||||
resultType = CheckRealImagOperand(*this, Input, OpLoc, Opc == UO_Real);
|
||||
// _Real maps ordinary l-values into ordinary l-values. _Imag maps
|
||||
// ordinary complex l-values to ordinary l-values and all other values to
|
||||
// r-values.
|
||||
if (Input.isInvalid())
|
||||
return ExprError();
|
||||
if (Opc == UO_Real || Input.get()->getType()->isAnyComplexType()) {
|
||||
if (Input.get()->isGLValue() &&
|
||||
Input.get()->getObjectKind() == OK_Ordinary)
|
||||
VK = Input.get()->getValueKind();
|
||||
} else if (!getLangOpts().CPlusPlus) {
|
||||
// In C, a volatile scalar is read by __imag. In C++, it is not.
|
||||
Input = DefaultLvalueConversion(Input.get());
|
||||
}
|
||||
break;
|
||||
case UO_Extension:
|
||||
resultType = Input.get()->getType();
|
||||
VK = Input.get()->getValueKind();
|
||||
OK = Input.get()->getObjectKind();
|
||||
break;
|
||||
case UO_Coawait:
|
||||
// It's unnecessary to represent the pass-through operator co_await in the
|
||||
// AST; just return the input expression instead.
|
||||
assert(!Input.get()->getType()->isDependentType() &&
|
||||
"the co_await expression must be non-dependant before "
|
||||
"building operator co_await");
|
||||
return Input;
|
||||
}
|
||||
break;
|
||||
case UO_Extension:
|
||||
resultType = Input.get()->getType();
|
||||
VK = Input.get()->getValueKind();
|
||||
OK = Input.get()->getObjectKind();
|
||||
break;
|
||||
case UO_Coawait:
|
||||
// It's unnecessary to represent the pass-through operator co_await in the
|
||||
// AST; just return the input expression instead.
|
||||
assert(!Input.get()->getType()->isDependentType() &&
|
||||
"the co_await expression must be non-dependant before "
|
||||
"building operator co_await");
|
||||
return Input;
|
||||
}
|
||||
if (resultType.isNull() || Input.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
@@ -16236,10 +16236,11 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
|
||||
return getSema().CreateBuiltinUnaryOp(OpLoc, Opc, First);
|
||||
}
|
||||
} else {
|
||||
if (!First->getType()->isOverloadableType() &&
|
||||
if (!First->isTypeDependent() && !Second->isTypeDependent() &&
|
||||
!First->getType()->isOverloadableType() &&
|
||||
!Second->getType()->isOverloadableType()) {
|
||||
// Neither of the arguments is an overloadable type, so try to
|
||||
// create a built-in binary operation.
|
||||
// Neither of the arguments is type-dependent or has an overloadable
|
||||
// type, so try to create a built-in binary operation.
|
||||
BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op);
|
||||
ExprResult Result
|
||||
= SemaRef.CreateBuiltinBinOp(OpLoc, Opc, First, Second);
|
||||
@@ -16250,12 +16251,8 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
|
||||
}
|
||||
}
|
||||
|
||||
// Add any functions found via argument-dependent lookup.
|
||||
Expr *Args[2] = { First, Second };
|
||||
unsigned NumArgs = 1 + (Second != nullptr);
|
||||
|
||||
// Create the overloaded operator invocation for unary operators.
|
||||
if (NumArgs == 1 || isPostIncDec) {
|
||||
if (!Second || isPostIncDec) {
|
||||
UnaryOperatorKind Opc
|
||||
= UnaryOperator::getOverloadedOpcode(Op, isPostIncDec);
|
||||
return SemaRef.CreateOverloadedUnaryOp(OpLoc, Opc, Functions, First,
|
||||
@@ -16264,8 +16261,8 @@ ExprResult TreeTransform<Derived>::RebuildCXXOperatorCallExpr(
|
||||
|
||||
// Create the overloaded operator invocation for binary operators.
|
||||
BinaryOperatorKind Opc = BinaryOperator::getOverloadedOpcode(Op);
|
||||
ExprResult Result = SemaRef.CreateOverloadedBinOp(
|
||||
OpLoc, Opc, Functions, Args[0], Args[1], RequiresADL);
|
||||
ExprResult Result = SemaRef.CreateOverloadedBinOp(OpLoc, Opc, Functions,
|
||||
First, Second, RequiresADL);
|
||||
if (Result.isInvalid())
|
||||
return ExprError();
|
||||
|
||||
|
||||
@@ -4261,9 +4261,9 @@ void TestNonADLCall3() {
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "type": {
|
||||
// CHECK-NEXT: "qualType": "<dependent type>"
|
||||
// CHECK-NEXT: "qualType": "V"
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "valueCategory": "prvalue",
|
||||
// CHECK-NEXT: "valueCategory": "lvalue",
|
||||
// CHECK-NEXT: "isPostfix": false,
|
||||
// CHECK-NEXT: "opcode": "*",
|
||||
// CHECK-NEXT: "canOverflow": false,
|
||||
|
||||
@@ -282,7 +282,7 @@ void PrimaryExpressions(Ts... a) {
|
||||
// CHECK-NEXT: CompoundStmt
|
||||
// CHECK-NEXT: FieldDecl 0x{{[^ ]*}} <col:8> col:8 implicit 'V'
|
||||
// CHECK-NEXT: ParenListExpr 0x{{[^ ]*}} <col:8> 'NULL TYPE'
|
||||
// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <col:8> '<dependent type>' prefix '*' cannot overflow
|
||||
// CHECK-NEXT: UnaryOperator 0x{{[^ ]*}} <col:8> 'V' lvalue prefix '*' cannot overflow
|
||||
// CHECK-NEXT: CXXThisExpr 0x{{[^ ]*}} <col:8> 'V *' this
|
||||
}
|
||||
};
|
||||
|
||||
@@ -81,7 +81,7 @@ template <typename... Ts> void test(Ts... a) {
|
||||
// CHECK-NEXT: | | | `-CompoundStmt {{.*}} <col:15, col:16>
|
||||
// CHECK-NEXT: | | `-FieldDecl {{.*}} <col:8> col:8{{( imported)?}} implicit 'V'
|
||||
// CHECK-NEXT: | |-ParenListExpr {{.*}} <col:8> 'NULL TYPE'
|
||||
// CHECK-NEXT: | | `-UnaryOperator {{.*}} <col:8> '<dependent type>' prefix '*' cannot overflow
|
||||
// CHECK-NEXT: | | `-UnaryOperator {{.*}} <col:8> 'V' lvalue prefix '*' cannot overflow
|
||||
// CHECK-NEXT: | | `-CXXThisExpr {{.*}} <col:8> 'V *' this
|
||||
// CHECK-NEXT: | `-CompoundStmt {{.*}} <col:15, col:16>
|
||||
// CHECK-NEXT: |-DeclStmt {{.*}} <line:22:3, col:11>
|
||||
|
||||
65
clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp
Normal file
65
clang/test/CXX/expr/expr.unary/expr.unary.general/p1.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
// RUN: %clang_cc1 -Wno-unused -fsyntax-only %s -verify
|
||||
|
||||
struct A {
|
||||
void operator*();
|
||||
void operator+();
|
||||
void operator-();
|
||||
void operator!();
|
||||
void operator~();
|
||||
void operator&();
|
||||
void operator++();
|
||||
void operator--();
|
||||
};
|
||||
|
||||
struct B { };
|
||||
|
||||
template<typename T, typename U>
|
||||
void dependent(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) {
|
||||
*t;
|
||||
+t;
|
||||
-t;
|
||||
!t;
|
||||
~t;
|
||||
&t;
|
||||
++t;
|
||||
--t;
|
||||
|
||||
*pt;
|
||||
+pt;
|
||||
-pt; // expected-error {{invalid argument type 'T *' to unary expression}}
|
||||
!pt;
|
||||
~pt; // expected-error {{invalid argument type 'T *' to unary expression}}
|
||||
&pt;
|
||||
++pt;
|
||||
--pt;
|
||||
|
||||
*mpt; // expected-error {{indirection requires pointer operand ('T U::*' invalid)}}
|
||||
+mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}}
|
||||
-mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}}
|
||||
!mpt;
|
||||
~mpt; // expected-error {{invalid argument type 'T U::*' to unary expression}}
|
||||
&mpt;
|
||||
++mpt; // expected-error {{cannot increment value of type 'T U::*'}}
|
||||
--mpt; // expected-error {{cannot decrement value of type 'T U::*'}}
|
||||
|
||||
*ft;
|
||||
+ft;
|
||||
-ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}}
|
||||
!ft;
|
||||
~ft; // expected-error {{invalid argument type 'T (*)()' to unary expression}}
|
||||
&ft;
|
||||
++ft; // expected-error {{cannot increment value of type 'T ()'}}
|
||||
--ft; // expected-error {{cannot decrement value of type 'T ()'}}
|
||||
|
||||
*at;
|
||||
+at;
|
||||
-at; // expected-error {{invalid argument type 'T *' to unary expression}}
|
||||
!at;
|
||||
~at; // expected-error {{invalid argument type 'T *' to unary expression}}
|
||||
&at;
|
||||
++at; // expected-error {{cannot increment value of type 'T[4]'}}
|
||||
--at; // expected-error {{cannot decrement value of type 'T[4]'}}
|
||||
}
|
||||
|
||||
// Make sure we only emit diagnostics once.
|
||||
template void dependent(A t, A* pt, A B::* mpt, A(&ft)(), A(&at)[4]);
|
||||
@@ -1,41 +1,139 @@
|
||||
// RUN: %clang_cc1 -std=c++17 -ast-dump %s -ast-dump-filter Test | FileCheck %s
|
||||
// RUN: %clang_cc1 -std=c++17 -Wno-unused -ast-dump %s -ast-dump-filter Test | FileCheck %s
|
||||
|
||||
struct A{};
|
||||
namespace Test {
|
||||
template<typename T, typename U>
|
||||
void Unary(T t, T* pt, T U::* mpt, T(&ft)(), T(&at)[4]) {
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '*' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
*t;
|
||||
|
||||
template <typename T, typename U>
|
||||
auto Test(T* pt, U* pu) {
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '*'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
(void)*pt;
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '+' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
+t;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '++'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
(void)(++pt);
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '-' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
-t;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '+'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
(void)(+pt);
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '!' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
!t;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '+'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
|
||||
(void)(pt + 3);
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '~' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
~t;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
(void)(pt - pt);
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
&t;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
|
||||
(void)(pt - pu);
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '++' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
++t;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '=='
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
|
||||
(void)(pt == pu);
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' lvalue prefix '--' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T' lvalue ParmVar {{.*}} 't' 'T'
|
||||
--t;
|
||||
|
||||
}
|
||||
// CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
*pt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
+pt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean>
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <LValueToRValue>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
!pt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
&pt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '++' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
++pt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T *' lvalue prefix '--' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
--pt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <MemberPointerToBoolean>
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T U::*' <LValueToRValue>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*'
|
||||
!mpt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T U::*' lvalue ParmVar {{.*}} 'mpt' 'T U::*'
|
||||
&mpt;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T ()' lvalue prefix '*' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
|
||||
*ft;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T (*)()' prefix '+' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
|
||||
+ft;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean>
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T (*)()' <FunctionToPointerDecay>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
|
||||
!ft;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T ()' lvalue ParmVar {{.*}} 'ft' 'T (&)()'
|
||||
&ft;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T' lvalue prefix '*' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
|
||||
*at;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'T *' prefix '+' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
|
||||
+at;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} 'bool' prefix '!' cannot overflow
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool' <PointerToBoolean>
|
||||
// CHECK-NEXT: ImplicitCastExpr {{.*}} 'T *' <ArrayToPointerDecay>
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
|
||||
!at;
|
||||
|
||||
// CHECK: UnaryOperator {{.*}} '<dependent type>' prefix '&' cannot overflow
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T[4]' lvalue ParmVar {{.*}} 'at' 'T (&)[4]'
|
||||
&at;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
void Binary(T* pt, U* pu) {
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '+'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3
|
||||
pt + 3;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
pt - pt;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '-'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
|
||||
pt - pu;
|
||||
|
||||
// CHECK: BinaryOperator {{.*}} '<dependent type>' '=='
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'T *' lvalue ParmVar {{.*}} 'pt' 'T *'
|
||||
// CHECK-NEXT: DeclRefExpr {{.*}} 'U *' lvalue ParmVar {{.*}} 'pu' 'U *'
|
||||
pt == pu;
|
||||
}
|
||||
} // namespace Test
|
||||
|
||||
@@ -15,6 +15,6 @@ void f(int i, float f, bool b, char c, int* pi, A* pa, T* pt) {
|
||||
|
||||
(void)-pi; // expected-error {{invalid argument type}}
|
||||
(void)-pa; // expected-error {{invalid argument type}}
|
||||
(void)-pt; // FIXME: we should be able to give an error here.
|
||||
(void)-pt; // expected-error {{invalid argument type}}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,6 @@ void f(int i, float f, bool b, char c, int* pi, T* pt) {
|
||||
(void)~b;
|
||||
(void)~c;
|
||||
(void)~pi; // expected-error {{invalid argument type}}
|
||||
(void)~pt; // FIXME: we should be able to give an error here.
|
||||
(void)~pt; // expected-error {{invalid argument type}}
|
||||
}
|
||||
|
||||
|
||||
173
clang/test/CXX/over/over.oper/over.oper.general/p1.cpp
Normal file
173
clang/test/CXX/over/over.oper/over.oper.general/p1.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -verify -Wno-unused %s
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator->*(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator+(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator-(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator*(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator/(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator%(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator^(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator&(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator|(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator+=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator-=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator*=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator/=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator%=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator^=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator&=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator|=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator==(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator!=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator<(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator>(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator<=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator>=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator<=>(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator&&(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator||(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator<<(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator>>(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator<<=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator>>=(T, U);
|
||||
|
||||
template<typename T, typename U>
|
||||
void operator,(T, U);
|
||||
|
||||
template<typename T>
|
||||
void operator*(T);
|
||||
|
||||
template<typename T>
|
||||
void operator&(T);
|
||||
|
||||
template<typename T>
|
||||
void operator+(T);
|
||||
|
||||
template<typename T>
|
||||
void operator-(T);
|
||||
|
||||
template<typename T>
|
||||
void operator!(T);
|
||||
|
||||
template<typename T>
|
||||
void operator~(T);
|
||||
|
||||
template<typename T>
|
||||
void operator++(T);
|
||||
|
||||
template<typename T>
|
||||
void operator--(T);
|
||||
|
||||
template<typename T>
|
||||
void operator++(T, int);
|
||||
|
||||
template<typename T>
|
||||
void operator--(T, int);
|
||||
|
||||
template<typename T>
|
||||
void f(int *x) {
|
||||
[&](auto *y) {
|
||||
*y;
|
||||
&y;
|
||||
+y;
|
||||
-y; // expected-error {{invalid argument type 'auto *' to unary expression}}
|
||||
!y;
|
||||
~y; // expected-error {{invalid argument type 'auto *' to unary expression}}
|
||||
++y;
|
||||
--y;
|
||||
y++;
|
||||
y--;
|
||||
y->*x;
|
||||
y + x;
|
||||
y - x;
|
||||
y * x;
|
||||
y / x;
|
||||
y % x;
|
||||
y ^ x;
|
||||
y & x;
|
||||
y | x;
|
||||
y += x;
|
||||
y -= x;
|
||||
y *= x;
|
||||
y /= x;
|
||||
y %= x;
|
||||
y ^= x;
|
||||
y &= x;
|
||||
y |= x;
|
||||
y == x;
|
||||
y != x;
|
||||
y < x;
|
||||
y > x;
|
||||
y <= x;
|
||||
y >= x;
|
||||
y <=> x;
|
||||
y && x;
|
||||
y || x;
|
||||
y << x;
|
||||
y >> x;
|
||||
y <<= x;
|
||||
y >>= x;
|
||||
y, x;
|
||||
};
|
||||
}
|
||||
|
||||
template void f<int>(int*);
|
||||
@@ -357,17 +357,14 @@ namespace N0 {
|
||||
a->A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
|
||||
a->B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
|
||||
|
||||
// FIXME: An overloaded unary 'operator*' is built for these
|
||||
// even though the operand is a pointer (to a dependent type).
|
||||
// Type::isOverloadableType should return false for such cases.
|
||||
(*this).x4;
|
||||
(*this).B::x4;
|
||||
(*this).A::x4;
|
||||
(*this).B::A::x4;
|
||||
(*this).f4();
|
||||
(*this).B::f4();
|
||||
(*this).A::f4();
|
||||
(*this).B::A::f4();
|
||||
(*this).x4; // expected-error{{no member named 'x4' in 'B<T>'}}
|
||||
(*this).B::x4; // expected-error{{no member named 'x4' in 'B<T>'}}
|
||||
(*this).A::x4; // expected-error{{no member named 'x4' in 'N0::A'}}
|
||||
(*this).B::A::x4; // expected-error{{no member named 'x4' in 'N0::A'}}
|
||||
(*this).f4(); // expected-error{{no member named 'f4' in 'B<T>'}}
|
||||
(*this).B::f4(); // expected-error{{no member named 'f4' in 'B<T>'}}
|
||||
(*this).A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
|
||||
(*this).B::A::f4(); // expected-error{{no member named 'f4' in 'N0::A'}}
|
||||
|
||||
b.x4; // expected-error{{no member named 'x4' in 'B<T>'}}
|
||||
b.B::x4; // expected-error{{no member named 'x4' in 'B<T>'}}
|
||||
@@ -399,15 +396,13 @@ namespace N1 {
|
||||
f<0>();
|
||||
this->f<0>();
|
||||
a->f<0>();
|
||||
// FIXME: This should not require 'template'!
|
||||
(*this).f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
|
||||
(*this).f<0>();
|
||||
b.f<0>();
|
||||
|
||||
x.f<0>();
|
||||
this->x.f<0>();
|
||||
a->x.f<0>();
|
||||
// FIXME: This should not require 'template'!
|
||||
(*this).x.f<0>(); // expected-error{{missing 'template' keyword prior to dependent template name 'f'}}
|
||||
(*this).x.f<0>();
|
||||
b.x.f<0>();
|
||||
|
||||
// FIXME: None of these should require 'template'!
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
#define NODEREF __attribute__((noderef))
|
||||
|
||||
template <typename T>
|
||||
int func(T NODEREF *a) { // expected-note 2 {{a declared here}}
|
||||
return *a + 1; // expected-warning 2 {{dereferencing a; was declared with a 'noderef' type}}
|
||||
int func(T NODEREF *a) { // expected-note 3 {{a declared here}}
|
||||
return *a + 1; // expected-warning 3 {{dereferencing a; was declared with a 'noderef' type}}
|
||||
}
|
||||
|
||||
void func() {
|
||||
|
||||
@@ -19,7 +19,7 @@ struct S {
|
||||
// new and delete are implicitly static
|
||||
void *operator new(this unsigned long); // expected-error{{an explicit object parameter cannot appear in a static function}}
|
||||
void operator delete(this void*); // expected-error{{an explicit object parameter cannot appear in a static function}}
|
||||
|
||||
|
||||
void g(this auto) const; // expected-error{{explicit object member function cannot have 'const' qualifier}}
|
||||
void h(this auto) &; // expected-error{{explicit object member function cannot have '&' qualifier}}
|
||||
void i(this auto) &&; // expected-error{{explicit object member function cannot have '&&' qualifier}}
|
||||
@@ -198,9 +198,7 @@ void func(int i) {
|
||||
void TestMutationInLambda() {
|
||||
[i = 0](this auto &&){ i++; }();
|
||||
[i = 0](this auto){ i++; }();
|
||||
[i = 0](this const auto&){ i++; }();
|
||||
// expected-error@-1 {{cannot assign to a variable captured by copy in a non-mutable lambda}}
|
||||
// expected-note@-2 {{in instantiation of}}
|
||||
[i = 0](this const auto&){ i++; }(); // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
|
||||
|
||||
int x;
|
||||
const auto l1 = [x](this auto&) { x = 42; }; // expected-error {{cannot assign to a variable captured by copy in a non-mutable lambda}}
|
||||
|
||||
@@ -18,7 +18,7 @@ int test_specs(A<float, float> *a1, A<float, int> *a2) {
|
||||
return a1->x + a2->y;
|
||||
}
|
||||
|
||||
int test_incomplete_specs(A<double, double> *a1,
|
||||
int test_incomplete_specs(A<double, double> *a1,
|
||||
A<double> *a2)
|
||||
{
|
||||
(void)a1->x; // expected-error{{member access into incomplete type}}
|
||||
@@ -39,7 +39,7 @@ template <> struct X<int, int> { int foo(); }; // #1
|
||||
template <> struct X<float> { int bar(); }; // #2
|
||||
|
||||
typedef int int_type;
|
||||
void testme(X<int_type> *x1, X<float, int> *x2) {
|
||||
void testme(X<int_type> *x1, X<float, int> *x2) {
|
||||
(void)x1->foo(); // okay: refers to #1
|
||||
(void)x2->bar(); // okay: refers to #2
|
||||
}
|
||||
@@ -53,7 +53,7 @@ struct A<char> {
|
||||
A<char>::A() { }
|
||||
|
||||
// Make sure we can see specializations defined before the primary template.
|
||||
namespace N{
|
||||
namespace N{
|
||||
template<typename T> struct A0;
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ namespace M {
|
||||
template<> struct ::A<long double>; // expected-error{{must occur at global scope}}
|
||||
}
|
||||
|
||||
template<> struct N::B<char> {
|
||||
template<> struct N::B<char> {
|
||||
int testf(int x) { return f(x); }
|
||||
};
|
||||
|
||||
@@ -138,9 +138,9 @@ namespace PR18009 {
|
||||
|
||||
template <typename T> struct C {
|
||||
template <int N, int M> struct S;
|
||||
template <int N> struct S<N, N ? **(T(*)[N])0 : 0> {}; // expected-error {{depends on a template parameter of the partial specialization}}
|
||||
template <int N> struct S<N, N ? **(T(*)[N])0 : 0> {}; // ok
|
||||
};
|
||||
C<int> c; // expected-note {{in instantiation of}}
|
||||
C<int> c;
|
||||
|
||||
template<int A> struct outer {
|
||||
template<int B, int C> struct inner {};
|
||||
|
||||
@@ -1572,9 +1572,9 @@ TEST_P(ASTMatchersTest, IsArrow_MatchesMemberVariablesViaArrow) {
|
||||
EXPECT_TRUE(
|
||||
matches("template <class T> class Y { void x() { this->m; } int m; };",
|
||||
memberExpr(isArrow())));
|
||||
EXPECT_TRUE(
|
||||
notMatches("template <class T> class Y { void x() { (*this).m; } };",
|
||||
cxxDependentScopeMemberExpr(isArrow())));
|
||||
EXPECT_TRUE(notMatches(
|
||||
"template <class T> class Y { void x() { (*this).m; } int m; };",
|
||||
memberExpr(isArrow())));
|
||||
}
|
||||
|
||||
TEST_P(ASTMatchersTest, IsArrow_MatchesStaticMemberVariablesViaArrow) {
|
||||
|
||||
Reference in New Issue
Block a user