Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -851,6 +851,7 @@ CanQualType OCLSamplerTy, OCLEventTy, OCLClkEventTy; CanQualType OCLQueueTy, OCLNDRangeTy, OCLReserveIDTy; CanQualType OMPArraySectionTy; + CanQualType MSPropertySubscriptTy; // Types for deductions in C++0x [stmt.ranged]'s desugaring. Built on demand. mutable QualType AutoDeductTy; // Deduction against 'auto'. Index: include/clang/AST/BuiltinTypes.def =================================================================== --- include/clang/AST/BuiltinTypes.def +++ include/clang/AST/BuiltinTypes.def @@ -248,8 +248,11 @@ // A placeholder type for OpenMP array sections. PLACEHOLDER_TYPE(OMPArraySection, OMPArraySectionTy) +// A placeholder type for MS property subscript expression. +PLACEHOLDER_TYPE(MSPropertySubscript, MSPropertySubscriptTy) + #ifdef LAST_BUILTIN_TYPE -LAST_BUILTIN_TYPE(OMPArraySection) +LAST_BUILTIN_TYPE(MSPropertySubscript) #undef LAST_BUILTIN_TYPE #endif Index: include/clang/Serialization/ASTBitCodes.h =================================================================== --- include/clang/Serialization/ASTBitCodes.h +++ include/clang/Serialization/ASTBitCodes.h @@ -797,7 +797,10 @@ /// \brief OpenCL reserve_id type. PREDEF_TYPE_RESERVE_ID_ID = 54, /// \brief The placeholder type for OpenMP array section. - PREDEF_TYPE_OMP_ARRAY_SECTION = 55 + PREDEF_TYPE_OMP_ARRAY_SECTION = 55, + /// \brief The placeholder type for MS declspec property subscript pseudo + /// expression. + PREDEF_TYPE_MS_PROPERTY_SUBSCRIPT = 56, }; /// \brief The number of predefined type IDs that are reserved for Index: lib/AST/ASTContext.cpp =================================================================== --- lib/AST/ASTContext.cpp +++ lib/AST/ASTContext.cpp @@ -1055,6 +1055,9 @@ if (LangOpts.OpenMP) InitBuiltinType(OMPArraySectionTy, BuiltinType::OMPArraySection); + if(LangOpts.MSVCCompat) + InitBuiltinType(MSPropertySubscriptTy, BuiltinType::MSPropertySubscript); + // C99 6.2.5p11. FloatComplexTy = getComplexType(FloatTy); DoubleComplexTy = getComplexType(DoubleTy); Index: lib/AST/Type.cpp =================================================================== --- lib/AST/Type.cpp +++ lib/AST/Type.cpp @@ -2597,6 +2597,8 @@ return "reserve_id_t"; case OMPArraySection: return ""; + case MSPropertySubscript: + return ""; } llvm_unreachable("Invalid builtin type."); @@ -3554,6 +3556,7 @@ case BuiltinType::BuiltinFn: case BuiltinType::NullPtr: case BuiltinType::OMPArraySection: + case BuiltinType::MSPropertySubscript: return false; } Index: lib/AST/TypeLoc.cpp =================================================================== --- lib/AST/TypeLoc.cpp +++ lib/AST/TypeLoc.cpp @@ -353,6 +353,7 @@ case BuiltinType::OCLReserveID: case BuiltinType::BuiltinFn: case BuiltinType::OMPArraySection: + case BuiltinType::MSPropertySubscript: return TST_unspecified; } Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -3917,7 +3917,21 @@ // operand might be an overloadable type, in which case the overload // resolution for the operator overload should get the first crack // at the overload. - if (base->getType()->isNonOverloadPlaceholderType()) { + // MSDN, property (C++) + // https://msdn.microsoft.com/en-us/library/yhfk0thd(v=vs.120).aspx + // This attribute can also be used in the declaration of an empty array in a + // class or structure definition. For example: + // __declspec(property(get=GetX, put=PutX)) int x[]; + // The above statement indicates that x[] can be used with one or more array + // indices. In this case, i=p->x[a][b] will be turned into i=p->GetX(a, b), + // and p->x[a][b] = i will be turned into p->PutX(a, b, i); + auto *MSProp = dyn_cast(base->IgnoreParens()); + bool IsMSPropertySubscript = + (MSProp && MSProp->getPropertyDecl()->getType()->isArrayType()) || + base->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript); + if (!IsMSPropertySubscript && + base->getType()->isNonOverloadPlaceholderType()) { ExprResult result = CheckPlaceholderExpr(base); if (result.isInvalid()) return ExprError(); base = result.get(); @@ -4093,7 +4107,12 @@ Expr *RHSExp = Idx; // Perform default conversions. - if (!LHSExp->getType()->getAs()) { + auto *MSProp = dyn_cast(LHSExp->IgnoreParens()); + bool IsMSPropertySubscript = + (MSProp && MSProp->getPropertyDecl()->getType()->isArrayType()) || + LHSExp->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript); + if (!LHSExp->getType()->getAs() && !IsMSPropertySubscript) { ExprResult Result = DefaultFunctionArrayLvalueConversion(LHSExp); if (Result.isInvalid()) return ExprError(); @@ -4118,6 +4137,10 @@ BaseExpr = LHSExp; IndexExpr = RHSExp; ResultType = Context.DependentTy; + } else if (IsMSPropertySubscript) { + BaseExpr = LHSExp; + IndexExpr = RHSExp; + ResultType = Context.MSPropertySubscriptTy; } else if (const PointerType *PTy = LHSTy->getAs()) { BaseExpr = LHSExp; IndexExpr = RHSExp; @@ -4707,6 +4730,7 @@ return false; // Pseudo-objects should be converted as soon as possible. + case BuiltinType::MSPropertySubscript: case BuiltinType::PseudoObject: return true; @@ -4849,7 +4873,8 @@ return new (Context) CallExpr(Context, Fn, None, Context.VoidTy, VK_RValue, RParenLoc); } - if (Fn->getType() == Context.PseudoObjectTy) { + if (Fn->getType() == Context.PseudoObjectTy || + Fn->getType() == Context.MSPropertySubscriptTy) { ExprResult result = CheckPlaceholderExpr(Fn); if (result.isInvalid()) return ExprError(); Fn = result.get(); @@ -9355,7 +9380,8 @@ /// CheckForModifiableLvalue - Verify that E is a modifiable lvalue. If not, /// emit an error and return true. If so, return false. static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) { - assert(!E->hasPlaceholderType(BuiltinType::PseudoObject)); + assert(!E->hasPlaceholderType(BuiltinType::PseudoObject) && + !E->hasPlaceholderType(BuiltinType::MSPropertySubscript)); SourceLocation OrigLoc = Loc; Expr::isModifiableLvalueResult IsLV = E->isModifiableLvalue(S.Context, &Loc); @@ -9496,7 +9522,8 @@ QualType Sema::CheckAssignmentOperands(Expr *LHSExpr, ExprResult &RHS, SourceLocation Loc, QualType CompoundType) { - assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject)); + assert(!LHSExpr->hasPlaceholderType(BuiltinType::PseudoObject) && + !LHSExpr->hasPlaceholderType(BuiltinType::MSPropertySubscript)); // Verify that LHS is a modifiable lvalue, and emit error if not. if (CheckForModifiableLvalue(LHSExpr, Loc, *this)) @@ -10678,7 +10705,8 @@ // Handle pseudo-objects in the LHS. if (const BuiltinType *pty = LHSExpr->getType()->getAsPlaceholderType()) { // Assignments with a pseudo-object l-value need special analysis. - if (pty->getKind() == BuiltinType::PseudoObject && + if ((pty->getKind() == BuiltinType::PseudoObject || + pty->getKind() == BuiltinType::MSPropertySubscript) && BinaryOperator::isAssignmentOp(Opc)) return checkPseudoObjectAssignment(S, OpLoc, Opc, LHSExpr, RHSExpr); @@ -10968,7 +10996,8 @@ // overloaded-operator check considers the right type. if (const BuiltinType *pty = Input->getType()->getAsPlaceholderType()) { // Increment and decrement of pseudo-object references. - if (pty->getKind() == BuiltinType::PseudoObject && + if ((pty->getKind() == BuiltinType::PseudoObject || + pty->getKind() == BuiltinType::MSPropertySubscript) && UnaryOperator::isIncrementDecrementOp(Opc)) return checkPseudoObjectIncDec(S, OpLoc, Opc, Input); @@ -14477,6 +14506,7 @@ return diagnoseUnknownAnyExpr(*this, E); // Pseudo-objects. + case BuiltinType::MSPropertySubscript: case BuiltinType::PseudoObject: return checkPseudoObjectRValue(E); Index: lib/Sema/SemaPseudoObject.cpp =================================================================== --- lib/Sema/SemaPseudoObject.cpp +++ lib/Sema/SemaPseudoObject.cpp @@ -121,6 +121,15 @@ rebuiltExpr->isValueDependent()); } + if (e->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + auto *ASE = cast(e); + return new (S.Context) ArraySubscriptExpr( + rebuild(ASE->getBase()), ASE->getIdx(), ASE->getType(), + ASE->getValueKind(), ASE->getObjectKind(), ASE->getRBracketLoc()); + } + + llvm_unreachable("bad expression to rebuild!"); } }; @@ -329,11 +338,26 @@ class MSPropertyOpBuilder : public PseudoOpBuilder { MSPropertyRefExpr *RefExpr; OpaqueValueExpr *InstanceBase; + SmallVector ArgExprs; public: MSPropertyOpBuilder(Sema &S, MSPropertyRefExpr *refExpr) : PseudoOpBuilder(S, refExpr->getSourceRange().getBegin()), RefExpr(refExpr), InstanceBase(nullptr) {} + MSPropertyOpBuilder(Sema &S, Expr *E) + : PseudoOpBuilder(S, E->getSourceRange().getBegin()), + InstanceBase(nullptr) { + assert(E->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)); + auto *Base = E->IgnoreParens(); + while (Base->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + auto *ASE = cast(Base); + ArgExprs.insert(ArgExprs.begin(), ASE->getIdx()); + Base = ASE->getBase()->IgnoreParens(); + } + RefExpr = cast(Base); + } Expr *rebuildAndCaptureObject(Expr *) override; ExprResult buildGet() override; @@ -1432,7 +1456,6 @@ return ExprError(); } - MultiExprArg ArgExprs; return S.ActOnCallExpr(S.getCurScope(), GetterExpr.get(), RefExpr->getSourceRange().getBegin(), ArgExprs, RefExpr->getSourceRange().getEnd()); @@ -1462,7 +1485,6 @@ return ExprError(); } - SmallVector ArgExprs; ArgExprs.push_back(op); return S.ActOnCallExpr(S.getCurScope(), SetterExpr.get(), RefExpr->getSourceRange().getBegin(), ArgExprs, @@ -1488,6 +1510,10 @@ = dyn_cast(opaqueRef)) { MSPropertyOpBuilder builder(*this, refExpr); return builder.buildRValueOperation(E); + } else if (E->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + MSPropertyOpBuilder builder(*this, E); + return builder.buildRValueOperation(E); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -1514,6 +1540,10 @@ = dyn_cast(opaqueRef)) { MSPropertyOpBuilder builder(*this, refExpr); return builder.buildIncDecOperation(Sc, opcLoc, opcode, op); + } else if (opaqueRef->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + MSPropertyOpBuilder builder(*this, opaqueRef); + return builder.buildIncDecOperation(Sc, opcLoc, opcode, op); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -1545,7 +1575,11 @@ return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); } else if (MSPropertyRefExpr *refExpr = dyn_cast(opaqueRef)) { - MSPropertyOpBuilder builder(*this, refExpr); + MSPropertyOpBuilder builder(*this, refExpr); + return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); + } else if (opaqueRef->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + MSPropertyOpBuilder builder(*this, opaqueRef); return builder.buildAssignmentOperation(S, opcLoc, opcode, LHS, RHS); } else { llvm_unreachable("unknown pseudo-object kind!"); @@ -1576,6 +1610,16 @@ = dyn_cast(opaqueRef)) { OpaqueValueExpr *baseOVE = cast(refExpr->getBaseExpr()); return MSPropertyRefRebuilder(S, baseOVE->getSourceExpr()).rebuild(E); + } else if (opaqueRef->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + while (opaqueRef->getType()->isSpecificPlaceholderType( + BuiltinType::MSPropertySubscript)) { + opaqueRef = + cast(opaqueRef)->getBase()->IgnoreParens(); + } + auto *BaseOVE = cast( + cast(opaqueRef)->getBaseExpr()); + return MSPropertyRefRebuilder(S, BaseOVE->getSourceExpr()).rebuild(E); } else { llvm_unreachable("unknown pseudo-object kind!"); } @@ -1613,7 +1657,8 @@ bop->getObjectKind(), bop->getOperatorLoc(), false); } else { - assert(syntax->hasPlaceholderType(BuiltinType::PseudoObject)); + assert(syntax->hasPlaceholderType(BuiltinType::PseudoObject) || + syntax->hasPlaceholderType(BuiltinType::MSPropertySubscript)); return stripOpaqueValuesFromPseudoObjectRef(*this, syntax); } } Index: lib/Serialization/ASTCommon.cpp =================================================================== --- lib/Serialization/ASTCommon.cpp +++ lib/Serialization/ASTCommon.cpp @@ -187,6 +187,9 @@ case BuiltinType::OMPArraySection: ID = PREDEF_TYPE_OMP_ARRAY_SECTION; break; + case BuiltinType::MSPropertySubscript: + ID = PREDEF_TYPE_MS_PROPERTY_SUBSCRIPT; + break; } return TypeIdx(ID); Index: lib/Serialization/ASTReader.cpp =================================================================== --- lib/Serialization/ASTReader.cpp +++ lib/Serialization/ASTReader.cpp @@ -5955,6 +5955,10 @@ case PREDEF_TYPE_OMP_ARRAY_SECTION: T = Context.OMPArraySectionTy; break; + + case PREDEF_TYPE_MS_PROPERTY_SUBSCRIPT: + T = Context.MSPropertySubscriptTy; + break; } assert(!T.isNull() && "Unknown predefined type"); Index: test/CodeGenCXX/ms-property.cpp =================================================================== --- test/CodeGenCXX/ms-property.cpp +++ test/CodeGenCXX/ms-property.cpp @@ -1,4 +1,10 @@ // RUN: %clang_cc1 -emit-llvm -triple=x86_64-pc-win32 -fms-compatibility %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -emit-pch -o %t %s +// RUN: %clang_cc1 -emit-llvm -triple=x86_64-pc-win32 -fms-compatibility -include-pch %t -verify %s -o - | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER class Test1 { private: @@ -11,9 +17,42 @@ static Test1 *GetTest1() { return new Test1(10); } }; +class S { +public: + __declspec(property(get=GetX,put=PutX)) int x[]; + int GetX(int i, int j) { return i+j; } + void PutX(int i, int j, int k) { j = i = k; } +}; + +template +class St { +public: + __declspec(property(get=GetX,put=PutX)) T x[]; + T GetX(T i, T j) { return i+j; } + void PutX(T i, T j, T k) { j = i = k; } +}; + // CHECK-LABEL: main int main(int argc, char **argv) { + S *p1 = 0; + St *p2 = 0; + // CHECK: call i32 @"\01?GetX@S@@QEAAHHH@Z"(%class.S* %{{.+}}, i32 223, i32 11) + int j = p1->x[223][11]; + // CHECK: [[J:%.+]] = load i32, i32* % + // CHECK-NEXT: call void @"\01?PutX@S@@QEAAXHHH@Z"(%class.S* %{{.+}}, i32 23, i32 1, i32 [[J]]) + p1->x[23][1] = j; + // CHECK: call float @"\01?GetX@?$St@M@@QEAAMMM@Z"(%class.St* %{{.+}}, float 2.230000e+02, float 1.100000e+01) + float j1 = p2->x[223][11]; + // CHECK: [[J1:%.+]] = load float, float* % + // CHECK-NEXT: call void @"\01?PutX@?$St@M@@QEAAXMMM@Z"(%class.St* %{{.+}}, float 2.300000e+01, float 1.000000e+00, float [[J1]]) + p2->x[23][1] = j1; + // CHECK: [[GET:%.+]] = call float @"\01?GetX@?$St@M@@QEAAMMM@Z"(%class.St* %6, float 2.300000e+01, float 1.000000e+00) + // CHECK-NEXT: [[INC:%.+]] = fadd float [[GET]], 1.000000e+00 + // CHECK-NEXT: call void @"\01?PutX@?$St@M@@QEAAXMMM@Z"(%class.St* %6, float 2.300000e+01, float 1.000000e+00, float [[INC]]) + ++p2->x[23][1]; // CHECK: [[CALL:%.+]] = call %class.Test1* @"\01?GetTest1@Test1@@SAPEAV1@XZ"() // CHECK-NEXT: call i32 @"\01?get_x@Test1@@QEBAHXZ"(%class.Test1* [[CALL]]) return Test1::GetTest1()->X; } + +#endif //HEADER Index: test/SemaCXX/ms-property-error.cpp =================================================================== --- test/SemaCXX/ms-property-error.cpp +++ test/SemaCXX/ms-property-error.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -verify -fms-compatibility %s -fsyntax-only -o - + +class S { +public: + __declspec(property(get=GetX,put=PutX)) int x[]; + int GetX(int i, int j) { return i+j; } // expected-note {{'GetX' declared here}} + void PutX(int i, int j, int k) { j = i = k; } // expected-note {{'PutX' declared here}} +}; + +template +class St { +public: + __declspec(property(get=GetX,put=PutX)) T x[]; + T GetX(T i, T j) { return i+j; } // expected-note 3 {{'GetX' declared here}} + void PutX(T i, T j, T k) { j = i = k; } // expected-note 2 {{'PutX' declared here}} + ~St() { + x[1] = 0; // expected-error {{too few arguments to function call, expected 3, have 2}} + x[2][3] = 4; + ++x[2][3]; + x[1][2] = x[3][4][5]; // expected-error {{too many arguments to function call, expected 2, have 3}} + } +}; + +// CHECK-LABEL: main +int main(int argc, char **argv) { + S *p1 = 0; + St *p2 = 0; + St a; // expected-note {{in instantiation of member function 'St::~St' requested here}} + int j = (p1->x)[223][11][2]; // expected-error {{too many arguments to function call, expected 2, have 3}} + (p1->x[23]) = argc; // expected-error {{too few arguments to function call, expected 3, have 2}} + float j1 = (p2->x); // expected-error {{too few arguments to function call, expected 2, have 0}} + ((p2->x)[23])[1][2] = *argv; // expected-error {{too many arguments to function call, expected 3, have 4}} + return ++(((p2->x)[23])); // expected-error {{too few arguments to function call, expected 2, have 1}} +} Index: test/SemaCXX/ms-property.cpp =================================================================== --- test/SemaCXX/ms-property.cpp +++ test/SemaCXX/ms-property.cpp @@ -0,0 +1,58 @@ +// RUN: %clang_cc1 -ast-print -verify -triple=x86_64-pc-win32 -fms-compatibility %s -o - | FileCheck %s +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -emit-pch -o %t %s +// RUN: %clang_cc1 -triple=x86_64-pc-win32 -fms-compatibility -include-pch %t -verify %s -ast-print -o - | FileCheck %s +// expected-no-diagnostics + +#ifndef HEADER +#define HEADER + +class Test1 { +private: + int x_; + +public: + Test1(int x) : x_(x) {} + __declspec(property(get = get_x)) int X; + int get_x() const { return x_; } + static Test1 *GetTest1() { return new Test1(10); } +}; + +class S { +public: + __declspec(property(get=GetX,put=PutX)) int x[]; + int GetX(int i, int j) { return i+j; } + void PutX(int i, int j, int k) { j = i = k; } +}; + +template +class St { +public: + __declspec(property(get=GetX,put=PutX)) T x[]; + T GetX(T i, T j) { return i+j; } + void PutX(T i, T j, T k) { j = i = k; } + ~St() { x[0][0] = x[1][1]; } +}; + +// CHECK: this->x[0][0] = this->x[1][1]; +// CHECK: this->x[0][0] = this->x[1][1]; + +// CHECK-LABEL: main +int main(int argc, char **argv) { + S *p1 = 0; + St *p2 = 0; + // CHECK: St a; + St a; + // CHECK-NEXT: int j = (p1->x)[223][11]; + int j = (p1->x)[223][11]; + // CHECK-NEXT: (p1->x[23])[1] = j; + (p1->x[23])[1] = j; + // CHECK-NEXT: float j1 = (p2->x[223][11]); + float j1 = (p2->x[223][11]); + // CHECK-NEXT: ((p2->x)[23])[1] = j1; + ((p2->x)[23])[1] = j1; + // CHECK-NEXT: ++(((p2->x)[23])[1]); + ++(((p2->x)[23])[1]); + // CHECK-NEXT: return Test1::GetTest1()->X; + return Test1::GetTest1()->X; +} +#endif // HEADER