diff --git a/pcbnew/autorouter/ar_autoplacer.cpp b/pcbnew/autorouter/ar_autoplacer.cpp index ae1107461a..21522e32c6 100644 --- a/pcbnew/autorouter/ar_autoplacer.cpp +++ b/pcbnew/autorouter/ar_autoplacer.cpp @@ -360,9 +360,9 @@ void AR_AUTOPLACER::buildFpAreas( MODULE* aFootprint, int aFpClearance ) addFpBody( fpBBox.GetOrigin(), fpBBox.GetEnd(), layerMask ); // Trace pads + clearance areas. - for( auto pad : aFootprint->Pads() ) + for( D_PAD* pad : aFootprint->Pads() ) { - int margin = (m_matrix.m_GridRouting / 2) + pad->GetClearance(); + int margin = (m_matrix.m_GridRouting / 2) + pad->GetClearance( pad->GetLayer() ); addPad( pad, margin ); } } @@ -414,9 +414,9 @@ void AR_AUTOPLACER::genModuleOnRoutingMatrix( MODULE* Module ) CELL_IS_MODULE, AR_MATRIX::WRITE_OR_CELL ); // Trace pads + clearance areas. - for( auto pad : Module->Pads() ) + for( D_PAD* pad : Module->Pads() ) { - int margin = (m_matrix.m_GridRouting / 2) + pad->GetClearance(); + int margin = (m_matrix.m_GridRouting / 2) + pad->GetClearance( pad->GetLayer() ); m_matrix.PlacePad( pad, CELL_IS_MODULE, margin, AR_MATRIX::WRITE_OR_CELL ); } diff --git a/pcbnew/board_connected_item.cpp b/pcbnew/board_connected_item.cpp index 163f62b45a..59d7ec4fd1 100644 --- a/pcbnew/board_connected_item.cpp +++ b/pcbnew/board_connected_item.cpp @@ -92,7 +92,8 @@ NETCLASS* BOARD_CONNECTED_ITEM::GetEffectiveNetclass() const * LEVEL 2: Rules * LEVEL 3: Accumulated local settings, netclass settings, & board design settings */ -int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) const +int BOARD_CONNECTED_ITEM::GetClearance( PCB_LAYER_ID aLayer, BOARD_ITEM* aItem, + wxString* aSource ) const { BOARD* board = GetBoard(); int clearance = 0; @@ -122,7 +123,7 @@ int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) c // LEVEL 2: Rules // - if( GetRuleClearance( aItem, &clearance, aSource ) ) + if( GetRuleClearance( aItem, aLayer, &clearance, aSource ) ) return clearance; // LEVEL 3: Accumulated local settings, netclass settings, & board design settings @@ -163,17 +164,18 @@ int BOARD_CONNECTED_ITEM::GetClearance( BOARD_ITEM* aItem, wxString* aSource ) c } -bool BOARD_CONNECTED_ITEM::GetRuleClearance( BOARD_ITEM* aItem, int* aClearance, - wxString* aSource ) const +bool BOARD_CONNECTED_ITEM::GetRuleClearance( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, + int* aClearance, wxString* aSource ) const { - DRC_RULE* rule = GetRule( this, aItem, CLEARANCE_CONSTRAINT ); + const DRC_CONSTRAINT* constraint = GetConstraint( this, aItem, DRC_RULE_ID_CLEARANCE, aLayer, + aSource ); - if( rule ) + if( constraint ) { if( aSource ) - *aSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); + *aSource = wxString::Format( _( "'%s' rule" ), *aSource ); - *aClearance = rule->m_Clearance.Min; + *aClearance = constraint->m_Value.Min(); return true; } diff --git a/pcbnew/board_connected_item.h b/pcbnew/board_connected_item.h index bc8b5be1df..dee7e728d7 100644 --- a/pcbnew/board_connected_item.h +++ b/pcbnew/board_connected_item.h @@ -161,20 +161,24 @@ public: * returned clearance is the greater of this object's NETCLASS clearance and * aItem's NETCLASS clearance. If \a aItem is NULL, then this objects clearance * is returned. + * @param aLayer the layer in question * @param aItem is an optional BOARD_ITEM * @param aSource [out] optionally reports the source as a user-readable string * @return int - the clearance in internal units. */ - virtual int GetClearance( BOARD_ITEM* aItem = nullptr, wxString* aSource = nullptr ) const; + virtual int GetClearance( PCB_LAYER_ID aLayer, BOARD_ITEM* aItem = nullptr, + wxString* aSource = nullptr ) const; /** * Function GetRuleClearance * returns any rule-based clearance. + * @param aLayer the current layer under test * @param aClearance [out] the clearance value in internal units * @param aSource [out] reports the source as a user-readable string * @return true if a rule was fired */ - virtual bool GetRuleClearance( BOARD_ITEM* aItem, int* aClearance, wxString* aSource ) const; + virtual bool GetRuleClearance( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int* aClearance, + wxString* aSource ) const; /** * Function GetLocalClearanceOverrides diff --git a/pcbnew/board_design_settings.cpp b/pcbnew/board_design_settings.cpp index 530e3945db..3b5ac565ed 100644 --- a/pcbnew/board_design_settings.cpp +++ b/pcbnew/board_design_settings.cpp @@ -822,7 +822,13 @@ int BOARD_DESIGN_SETTINGS::GetBiggestClearanceValue() clearance = std::max( clearance, netclass.second->GetClearance() ); for( const DRC_RULE* rule : m_DRCRules ) - clearance = std::max( clearance, rule->m_Clearance.Min ); + { + for( const DRC_CONSTRAINT& constraint : rule->m_Constraints ) + { + if( constraint.m_Type == DRC_RULE_ID_CLEARANCE ) + clearance = std::max( clearance, constraint.m_Value.Min() ); + } + } return clearance; } diff --git a/pcbnew/class_pad.cpp b/pcbnew/class_pad.cpp index 13a4a568e0..69d15513db 100644 --- a/pcbnew/class_pad.cpp +++ b/pcbnew/class_pad.cpp @@ -825,7 +825,7 @@ void D_PAD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector } wxString source; - int clearance = GetClearance( nullptr, &source ); + int clearance = GetClearance( GetLayer(), nullptr, &source ); msg.Printf( _( "Min Clearance: %s" ), MessageTextFromValue( units, clearance, true ) ); msg2.Printf( _( "(from %s)" ), source ); diff --git a/pcbnew/class_track.cpp b/pcbnew/class_track.cpp index f485a6f6cb..677bdec130 100644 --- a/pcbnew/class_track.cpp +++ b/pcbnew/class_track.cpp @@ -133,15 +133,16 @@ void TRACK::GetWidthConstraints( int* aMin, int* aMax, wxString* aSource ) const // Not currently implemented // LEVEL 2: Rules - DRC_RULE* rule = GetRule( this, nullptr, TRACK_CONSTRAINT ); + const DRC_CONSTRAINT* constraint = GetConstraint( this, nullptr, DRC_RULE_ID_TRACK, m_Layer, + aSource ); - if( rule ) + if( constraint ) { - *aMin = rule->m_TrackConstraint.Min; - *aMax = rule->m_TrackConstraint.Max; + *aMin = constraint->m_Value.Min(); + *aMax = constraint->m_Value.Max(); if( aSource ) - *aSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); + *aSource = wxString::Format( _( "'%s' rule" ), *aSource ); return; } @@ -160,16 +161,17 @@ void TRACK::GetWidthConstraints( int* aMin, int* aMax, wxString* aSource ) const } -int VIA::GetMinAnnulus( wxString* aSource ) const +int VIA::GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const { - DRC_RULE* rule = GetRule( this, nullptr, ANNULUS_CONSTRAINT ); + const DRC_CONSTRAINT* constraint = GetConstraint( this, nullptr, DRC_RULE_ID_ANNULUS, aLayer, + aSource ); - if( rule ) + if( constraint ) { if( aSource ) - *aSource = wxString::Format( _( "'%s' rule" ), rule->m_Name ); + *aSource = wxString::Format( _( "'%s' rule" ), *aSource ); - return rule->m_MinAnnulusWidth; + return constraint->m_Value.Min(); } else { @@ -493,7 +495,7 @@ unsigned int TRACK::ViewGetLOD( int aLayer, KIGFX::VIEW* aView ) const const BOX2I TRACK::ViewBBox() const { BOX2I bbox = GetBoundingBox(); - bbox.Inflate( 2 * GetClearance() ); + bbox.Inflate( 2 * GetClearance( GetLayer() ) ); return bbox; } @@ -612,7 +614,7 @@ void TRACK::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector } } - int clearance = GetClearance( nullptr, &source ); + int clearance = GetClearance( GetLayer(), nullptr, &source ); msg.Printf( _( "Min Clearance: %s" ), MessageTextFromValue( units, clearance, true ) ); msg2.Printf( _( "(from %s)" ), source ); @@ -685,13 +687,13 @@ void VIA::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector& aList.emplace_back( _( "Drill" ), msg, RED ); - int clearance = GetClearance( nullptr, &source ); + int clearance = GetClearance( GetLayer(), nullptr, &source ); msg.Printf( _( "Min Clearance: %s" ), MessageTextFromValue( units, clearance, true ) ); msg2.Printf( _( "(from %s)" ), source ); aList.emplace_back( msg, msg2, BLACK ); - int minAnnulus = GetMinAnnulus( &source ); + int minAnnulus = GetMinAnnulus( GetLayer(), &source ); msg.Printf( _( "Min Annulus: %s" ), MessageTextFromValue( units, minAnnulus, true ) ); msg2.Printf( _( "(from %s)" ), source ); diff --git a/pcbnew/class_track.h b/pcbnew/class_track.h index 77124fd437..26f1471ffa 100644 --- a/pcbnew/class_track.h +++ b/pcbnew/class_track.h @@ -442,7 +442,7 @@ public: void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); } #endif - int GetMinAnnulus( wxString* aSource ) const; + int GetMinAnnulus( PCB_LAYER_ID aLayer, wxString* aSource ) const; /** * Function SetDrill diff --git a/pcbnew/class_zone.cpp b/pcbnew/class_zone.cpp index 2be6b1a58c..f06c95e174 100644 --- a/pcbnew/class_zone.cpp +++ b/pcbnew/class_zone.cpp @@ -336,7 +336,7 @@ int ZONE_CONTAINER::GetThermalReliefCopperBridge( D_PAD* aPad ) const } -int ZONE_CONTAINER::GetKeepouts( std::map* aSources ) const +int ZONE_CONTAINER::GetKeepouts( PCB_LAYER_ID aLayer, std::map* aSources ) const { wxString source; int keepouts = 0; @@ -370,41 +370,42 @@ int ZONE_CONTAINER::GetKeepouts( std::map* aSources ) const setFlag( DISALLOW_ZONES ); } - DRC_RULE* rule = GetRule( this, nullptr, DISALLOW_CONSTRAINT ); + const DRC_CONSTRAINT* constraint = GetConstraint( this, nullptr, DRC_RULE_ID_DISALLOW, aLayer, + &source ); - if( rule ) + if( constraint ) { if( aSources ) - source = wxString::Format( _( "'%s' rule" ), rule->m_Name ); + source = wxString::Format( _( "'%s' rule" ), source ); - if( ( rule->m_DisallowFlags & DISALLOW_VIAS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_VIAS ) > 0 ) setFlag( DISALLOW_VIAS ); - if( ( rule->m_DisallowFlags & DISALLOW_MICRO_VIAS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_MICRO_VIAS ) > 0 ) setFlag( DISALLOW_MICRO_VIAS ); - if( ( rule->m_DisallowFlags & DISALLOW_BB_VIAS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_BB_VIAS ) > 0 ) setFlag( DISALLOW_BB_VIAS ); - if( ( rule->m_DisallowFlags & DISALLOW_TRACKS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_TRACKS ) > 0 ) setFlag( DISALLOW_TRACKS ); - if( ( rule->m_DisallowFlags & DISALLOW_PADS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_PADS ) > 0 ) setFlag( DISALLOW_PADS ); - if( ( rule->m_DisallowFlags & DISALLOW_ZONES ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_ZONES ) > 0 ) setFlag( DISALLOW_ZONES ); - if( ( rule->m_DisallowFlags & DISALLOW_TEXTS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_TEXTS ) > 0 ) setFlag( DISALLOW_TEXTS ); - if( ( rule->m_DisallowFlags & DISALLOW_GRAPHICS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_GRAPHICS ) > 0 ) setFlag( DISALLOW_GRAPHICS ); - if( ( rule->m_DisallowFlags & DISALLOW_HOLES ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_HOLES ) > 0 ) setFlag( DISALLOW_HOLES ); - if( ( rule->m_DisallowFlags & DISALLOW_FOOTPRINTS ) > 0 ) + if( ( constraint->m_DisallowFlags & DISALLOW_FOOTPRINTS ) > 0 ) setFlag( DISALLOW_FOOTPRINTS ); } @@ -678,7 +679,7 @@ void ZONE_CONTAINER::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector( _( "Min Thickness" ), &ZONE_CONTAINER::SetMinThickness, &ZONE_CONTAINER::GetMinThickness, PROPERTY_DISPLAY::DISTANCE ) ); + propMgr.AddProperty( new PROPERTY( _( "Name" ), + &ZONE_CONTAINER::SetZoneName, &ZONE_CONTAINER::GetZoneName ) ); // TODO pad connection, thermal relief gap, thermal relief copper bridge } } _ZONE_CONTAINER_DESC; diff --git a/pcbnew/class_zone.h b/pcbnew/class_zone.h index a0c750a60b..beab0fe189 100644 --- a/pcbnew/class_zone.h +++ b/pcbnew/class_zone.h @@ -723,7 +723,7 @@ public: * flag. * @return a bitset of DISALLOW_* flags. */ - int GetKeepouts( std::map* aSources = nullptr ) const; + int GetKeepouts( PCB_LAYER_ID aLayer, std::map* aSources = nullptr ) const; void SetIsKeepout( bool aEnable ) { m_isKeepout = aEnable; } void SetDoNotAllowCopperPour( bool aEnable ) { m_doNotAllowCopperPour = aEnable; } diff --git a/pcbnew/dialogs/panel_setup_rules.cpp b/pcbnew/dialogs/panel_setup_rules.cpp index 20f5e106d8..36e08048eb 100644 --- a/pcbnew/dialogs/panel_setup_rules.cpp +++ b/pcbnew/dialogs/panel_setup_rules.cpp @@ -179,20 +179,37 @@ void PANEL_SETUP_RULES::onScintillaCharAdded( wxStyledTextEvent &aEvent ) if( sexprs.empty() ) tokens = "rule version"; else if( sexprs.top() == "rule" ) - tokens = "condition constraint disallow layer"; + tokens = "condition constraint layer"; else if( sexprs.top() == "constraint" ) tokens = "max min opt"; } else if( context == SEXPR_TOKEN ) { if( sexprs.empty() ) - /* badly formed grammar */; + { + /* badly formed grammar */ + } else if( sexprs.top() == "constraint" ) - tokens = "annulus_width clearance hole track_width"; - else if( sexprs.top() == "disallow" ) + { + tokens = "annulus_width clearance disallow hole track_width"; + } + else if( sexprs.top() == "disallow" + || sexprs.top() == "buried_via" + || sexprs.top() == "graphic" + || sexprs.top() == "hole" + || sexprs.top() == "micro_via" + || sexprs.top() == "pad" + || sexprs.top() == "text" + || sexprs.top() == "track" + || sexprs.top() == "via" + || sexprs.top() == "zone" ) + { tokens = "buried_via graphic hole micro_via pad text track via zone"; + } else if( sexprs.top() == "layer" ) + { tokens = "inner outer \"x\""; + } } else if( context == STRING && expr_context == STRUCT_REF ) { @@ -327,11 +344,16 @@ void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent ) msg << _( "Rule Clauses" ); msg << "" "
"
-            "(disallow <item_type>)\r"
             "(constraint <constraint_type> ...)\r"
             "(condition \"<expression>\")\r"
             "\r
" ""; + msg << _( "Constraint Types" ); + msg << "" + "
"
+            "clearance    annulus_width   track_width     hole     dissallow\r"
+            "\r
" + ""; msg << _( "Item Types" ); msg << "" "
"
@@ -340,17 +362,11 @@ void PANEL_SETUP_RULES::OnSyntaxHelp( wxHyperlinkEvent& aEvent )
             "hole          buried_via        graphic\r"
             "\r
" ""; - msg << _( "Constraint Types" ); - msg << "" - "
"
-            "clearance    annulus_width   track_width     hole\r"
-            "\r
" - ""; msg << _( "Examples" ); msg << "" "
"
             "(rule \"copper keepout\"\r"
-            "   (disallow track) (disallow via) (disallow zone)\r"
+            "   (constraint disallow track via zone)\r"
             "   (condition \"A.name == 'no_copper'\"))\r"
             "\r"
             "(rule \"BGA neckdown\"\r"
diff --git a/pcbnew/drc/drc.cpp b/pcbnew/drc/drc.cpp
index cf6f5f9296..37847dc39b 100644
--- a/pcbnew/drc/drc.cpp
+++ b/pcbnew/drc/drc.cpp
@@ -502,10 +502,7 @@ void DRC::testPadClearances( BOARD_COMMIT& aCommit )
             static DRAWSEGMENT dummyEdge;
             dummyEdge.SetLayer( Edge_Cuts );
 
-            if( pad->GetRuleClearance( &dummyEdge, &minClearance, &m_clearanceSource ) )
-            {
-                /* minClearance and m_clearanceSource set in GetRuleClearance() */;
-            }
+            pad->GetRuleClearance( &dummyEdge, pad->GetLayer(), &minClearance, &m_clearanceSource );
 
             for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
             {
@@ -723,7 +720,8 @@ void DRC::testZones( BOARD_COMMIT& aCommit )
             // Get clearance used in zone to zone test.  The policy used to
             // obtain that value is now part of the zone object itself by way of
             // ZONE_CONTAINER::GetClearance().
-            int zone2zoneClearance = zoneRef->GetClearance( zoneToTest, &m_clearanceSource );
+            int zone2zoneClearance = zoneRef->GetClearance( zoneRef->GetLayer(), zoneToTest,
+                                                            &m_clearanceSource );
 
             // Keepout areas have no clearance, so set zone2zoneClearance to 1
             // ( zone2zoneClearance = 0  can create problems in test functions)
@@ -907,12 +905,12 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
     SHAPE_RECT bboxShape( bbox.GetX(), bbox.GetY(), bbox.GetWidth(), bbox.GetHeight() );
 
     // Test tracks and vias
-    for( auto track : m_pcb->Tracks() )
+    for( TRACK* track : m_pcb->Tracks() )
     {
         if( !track->IsOnLayer( aItem->GetLayer() ) )
             continue;
 
-        int     minClearance = track->GetClearance( aItem, &m_clearanceSource );
+        int     minClearance = track->GetClearance( track->GetLayer(), aItem, &m_clearanceSource );
         int     actual = INT_MAX;
         wxPoint pos;
 
@@ -954,7 +952,7 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
     }
 
     // Test pads
-    for( auto pad : m_pcb->GetPads() )
+    for( D_PAD* pad : m_pcb->GetPads() )
     {
         if( !pad->IsOnLayer( aItem->GetLayer() ) )
             continue;
@@ -963,7 +961,7 @@ void DRC::testCopperDrawItem( BOARD_COMMIT& aCommit, BOARD_ITEM* aItem )
         if( drawItem && pad->GetParent() == drawItem->GetParent() )
             continue;
 
-        int minClearance = pad->GetClearance( aItem, &m_clearanceSource );
+        int minClearance = pad->GetClearance( aItem->GetLayer(), aItem, &m_clearanceSource );
         int actual = INT_MAX;
 
         // Fast test to detect a pad candidate inside the text bounding box
@@ -1136,7 +1134,8 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
 
             if( pad->GetDrillSize().x )
             {
-                int minClearance = aRefPad->GetClearance( nullptr, &m_clearanceSource );
+                int minClearance = aRefPad->GetClearance( aRefPad->GetLayer(), nullptr,
+                                                          &m_clearanceSource );
                 int actual;
 
                 if( aRefPad->Collide( pad->GetEffectiveHoleShape(), minClearance, &actual ) )
@@ -1159,7 +1158,8 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
 
             if( aRefPad->GetDrillSize().x )
             {
-                int minClearance = pad->GetClearance( nullptr, &m_clearanceSource );
+                int minClearance = pad->GetClearance( pad->GetLayer(), nullptr,
+                                                      &m_clearanceSource );
                 int actual;
 
                 if( pad->Collide( aRefPad->GetEffectiveHoleShape(), minClearance, &actual ) )
@@ -1216,25 +1216,28 @@ bool DRC::doPadToPadsDrc( BOARD_COMMIT& aCommit, D_PAD* aRefPad, D_PAD** aStart,
             continue;
         }
 
-        int  minClearance = aRefPad->GetClearance( pad, &m_clearanceSource );
-        int  clearanceAllowed = minClearance - m_pcb->GetDesignSettings().GetDRCEpsilon();
-        int  actual;
-
-        if( aRefPad->Collide( pad, clearanceAllowed, &actual ) )
+        for( PCB_LAYER_ID layer : aRefPad->GetLayerSet().Seq() )
         {
-            DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
+            int  minClearance = aRefPad->GetClearance( layer, pad, &m_clearanceSource );
+            int  clearanceAllowed = minClearance - m_pcb->GetDesignSettings().GetDRCEpsilon();
+            int  actual;
 
-            m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
-                          m_clearanceSource,
-                          MessageTextFromValue( userUnits(), minClearance, true ),
-                          MessageTextFromValue( userUnits(), actual, true ) );
+            if( aRefPad->Collide( pad, clearanceAllowed, &actual ) )
+            {
+                DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_CLEARANCE );
 
-            drcItem->SetErrorMessage( m_msg );
-            drcItem->SetItems( aRefPad, pad );
+                m_msg.Printf( drcItem->GetErrorText() + _( " (%s clearance %s; actual %s)" ),
+                              m_clearanceSource,
+                              MessageTextFromValue( userUnits(), minClearance, true ),
+                              MessageTextFromValue( userUnits(), actual, true ) );
 
-            MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefPad->GetPosition() );
-            addMarkerToPcb( aCommit, marker );
-            return false;
+                drcItem->SetErrorMessage( m_msg );
+                drcItem->SetItems( aRefPad, pad );
+
+                MARKER_PCB* marker = new MARKER_PCB( drcItem, aRefPad->GetPosition() );
+                addMarkerToPcb( aCommit, marker );
+                return false;
+            }
         }
     }
 
diff --git a/pcbnew/drc/drc_clearance_test_functions.cpp b/pcbnew/drc/drc_clearance_test_functions.cpp
index 2d6c4db42f..c360fdae64 100644
--- a/pcbnew/drc/drc_clearance_test_functions.cpp
+++ b/pcbnew/drc/drc_clearance_test_functions.cpp
@@ -57,7 +57,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
     {
         VIA *refvia = static_cast( aRefSeg );
         int viaAnnulus = ( refvia->GetWidth() - refvia->GetDrill() ) / 2;
-        int minAnnulus = refvia->GetMinAnnulus( &m_clearanceSource );
+        int minAnnulus = refvia->GetMinAnnulus( refvia->GetLayer(), &m_clearanceSource );
 
         // test if the via size is smaller than minimum
         if( refvia->GetViaType() == VIATYPE::MICROVIA )
@@ -280,20 +280,22 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
 
             if( pad->GetDrillSize().x > 0 )
             {
-                const SHAPE_SEGMENT* slot = pad->GetEffectiveHoleShape();
-                DRC_RULE*            rule = GetRule( aRefSeg, pad, CLEARANCE_CONSTRAINT );
-
+                const SHAPE_SEGMENT*  slot = pad->GetEffectiveHoleShape();
+                const DRC_CONSTRAINT* constraint = GetConstraint( aRefSeg, pad,
+                                                                  DRC_RULE_ID_CLEARANCE, refLayer,
+                                                                  &m_clearanceSource );
                 int minClearance;
                 int actual;
 
-                if( rule )
+                if( constraint )
                 {
-                    m_clearanceSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
-                    minClearance = rule->m_Clearance.Min;
+                    m_clearanceSource = wxString::Format( _( "'%s' rule" ), m_clearanceSource );
+                    minClearance = constraint->m_Value.Min();
                 }
                 else
                 {
-                    minClearance = aRefSeg->GetClearance( nullptr, &m_clearanceSource );
+                    minClearance = aRefSeg->GetClearance( refLayer, nullptr,
+                                                          &m_clearanceSource );
                 }
 
                 if( slot->Collide( &refSeg, minClearance + bds.GetDRCEpsilon(), &actual ) )
@@ -316,7 +318,8 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
                 }
             }
 
-            int minClearance = aRefSeg->GetClearance( pad, &m_clearanceSource );
+            int minClearance = aRefSeg->GetClearance( aRefSeg->GetLayer(), pad,
+                                                      &m_clearanceSource );
             int actual;
 
             if( pad->Collide( &refSeg, minClearance - bds.GetDRCEpsilon(), &actual ) )
@@ -383,7 +386,8 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
         if( !trackBB.Intersects( refSegBB ) )
             continue;
 
-        int           minClearance = aRefSeg->GetClearance( track, &m_clearanceSource );
+        int           minClearance = aRefSeg->GetClearance( aRefSeg->GetLayer(), track,
+                                                            &m_clearanceSource );
         int           actual;
         SHAPE_SEGMENT trackSeg( track->GetStart(), track->GetEnd(), track->GetWidth() );
 
@@ -447,7 +451,8 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
                 // (1 micron)
                 #define THRESHOLD_DIST Millimeter2iu( 0.001 )
 
-                int minClearance = aRefSeg->GetClearance( zone, &m_clearanceSource );
+                int minClearance = aRefSeg->GetClearance( aRefSeg->GetLayer(), zone,
+                                                          &m_clearanceSource );
                 int widths       = refSegWidth / 2;
                 int allowedDist  = minClearance + widths + THRESHOLD_DIST;
                 int actual;
@@ -483,10 +488,8 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
         static DRAWSEGMENT dummyEdge;
         dummyEdge.SetLayer( Edge_Cuts );
 
-        if( aRefSeg->GetRuleClearance( &dummyEdge, &minClearance, &m_clearanceSource ) )
-        {
-            /* minClearance and m_clearanceSource set in GetRuleClearance() */;
-        }
+        aRefSeg->GetRuleClearance( &dummyEdge, aRefSeg->GetLayer(), &minClearance,
+                                   &m_clearanceSource );
 
         SEG testSeg( aRefSeg->GetStart(), aRefSeg->GetEnd() );
         int halfWidth = refSegWidth / 2;
diff --git a/pcbnew/drc/drc_drilled_hole_tester.cpp b/pcbnew/drc/drc_drilled_hole_tester.cpp
index f1d8478e06..93c8e4f71c 100644
--- a/pcbnew/drc/drc_drilled_hole_tester.cpp
+++ b/pcbnew/drc/drc_drilled_hole_tester.cpp
@@ -84,21 +84,28 @@ bool DRC_DRILLED_HOLE_TESTER::checkPad( D_PAD* aPad )
     bool                   success = true;
     BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
 
-    int holeSize = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
+    // drilled holes go all the way through, so which layer we use shouldn't matter
+    PCB_LAYER_ID layer = F_Cu;
+    int          holeSize = std::min( aPad->GetDrillSize().x, aPad->GetDrillSize().y );
 
     if( holeSize == 0 )
         return true;
 
     if( !bds.Ignore( DRCE_TOO_SMALL_DRILL ) )
     {
-        int       minHole = bds.m_MinThroughDrill;
-        wxString  minHoleSource = _( "board minimum" );
-        DRC_RULE* rule = GetRule( aPad, nullptr, HOLE_CONSTRAINT );
+        int                   minHole;
+        const DRC_CONSTRAINT* constraint = GetConstraint( aPad, nullptr, DRC_RULE_ID_HOLE_SIZE,
+                                                          layer, &m_source );
 
-        if( rule )
+        if( constraint )
         {
-            minHole = rule->m_MinHole;
-            minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
+            minHole = constraint->m_Value.Min();
+            m_source = wxString::Format( _( "'%s' rule" ), m_source );
+        }
+        else
+        {
+            minHole = bds.m_MinThroughDrill;
+            m_source = _( "board minimum" );
         }
 
         if( holeSize < minHole )
@@ -106,7 +113,7 @@ bool DRC_DRILLED_HOLE_TESTER::checkPad( D_PAD* aPad )
             DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TOO_SMALL_DRILL );
 
             m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
-                          minHoleSource,
+                          m_source,
                           MessageTextFromValue( m_units, minHole, true ),
                           MessageTextFromValue( m_units, holeSize, true ) );
 
@@ -132,16 +139,24 @@ bool DRC_DRILLED_HOLE_TESTER::checkVia( VIA* via )
     bool                   success = true;
     BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
 
+    // drilled holes go all the way through, so which layer we use shouldn't matter
+    PCB_LAYER_ID layer = F_Cu;
+
     if( !bds.Ignore( DRCE_TOO_SMALL_DRILL ) )
     {
-        int       minHole = bds.m_MinThroughDrill;
-        wxString  minHoleSource = _( "board minimum" );
-        DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT );
+        int                   minHole;
+        const DRC_CONSTRAINT* constraint = GetConstraint( via, nullptr, DRC_RULE_ID_HOLE_SIZE,
+                                                          layer, &m_source );
 
-        if( rule )
+        if( constraint )
         {
-            minHole = rule->m_MinHole;
-            minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
+            minHole = constraint->m_Value.Min();
+            m_source = wxString::Format( _( "'%s' rule" ), m_source );
+        }
+        else
+        {
+            minHole = bds.m_MinThroughDrill;
+            m_source = _( "board minimum" );
         }
 
         if( via->GetDrillValue() < minHole )
@@ -149,7 +164,7 @@ bool DRC_DRILLED_HOLE_TESTER::checkVia( VIA* via )
             DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TOO_SMALL_DRILL );
 
             m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
-                          minHoleSource,
+                          m_source,
                           MessageTextFromValue( m_units, minHole, true ),
                           MessageTextFromValue( m_units, via->GetDrillValue(), true ) );
 
@@ -175,16 +190,25 @@ bool DRC_DRILLED_HOLE_TESTER::checkMicroVia( VIA* via )
     bool                   success = true;
     BOARD_DESIGN_SETTINGS& bds = m_board->GetDesignSettings();
 
+    // while microvia holes don't necessarily go all the way through, they can't be different
+    // sizes on different layers so we should still be safe enough using a fixed layer.
+    PCB_LAYER_ID layer = F_Cu;
+
     if( !bds.Ignore( DRCE_TOO_SMALL_MICROVIA_DRILL ) )
     {
-        int       minHole = bds.m_MicroViasMinDrill;
-        wxString  minHoleSource = _( "board minimum" );
-        DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT );
+        int                   minHole;
+        const DRC_CONSTRAINT* constraint = GetConstraint( via, nullptr, DRC_RULE_ID_HOLE_SIZE,
+                                                          layer, &m_source );
 
-        if( rule )
+        if( constraint )
         {
-            minHole = rule->m_MinHole;
-            minHoleSource = wxString::Format( _( "'%s' rule" ), rule->m_Name );
+            minHole = constraint->m_Value.Min();
+            m_source = wxString::Format( _( "'%s' rule" ), m_source );
+        }
+        else
+        {
+            minHole = bds.m_MicroViasMinDrill;
+            m_source = _( "board minimum" );
         }
 
         if(  via->GetDrillValue() < minHole )
@@ -192,7 +216,7 @@ bool DRC_DRILLED_HOLE_TESTER::checkMicroVia( VIA* via )
             DRC_ITEM* drcItem = DRC_ITEM::Create( DRCE_TOO_SMALL_MICROVIA_DRILL );
 
             m_msg.Printf( drcItem->GetErrorText() + _( " (%s %s; actual %s)" ),
-                          minHoleSource,
+                          m_source,
                           MessageTextFromValue( m_units, minHole, true ),
                           MessageTextFromValue( m_units, via->GetDrillValue(), true ) );
 
diff --git a/pcbnew/drc/drc_drilled_hole_tester.h b/pcbnew/drc/drc_drilled_hole_tester.h
index a23e6843eb..7f3f463b58 100644
--- a/pcbnew/drc/drc_drilled_hole_tester.h
+++ b/pcbnew/drc/drc_drilled_hole_tester.h
@@ -62,7 +62,8 @@ private:
     std::vector m_holes;
     int                       m_largestRadius;
 
-    wxString                  m_msg;    // Construct only once for performance
+    wxString                  m_source;    // Construct only once for performance
+    wxString                  m_msg;       // Construct only once for performance
 };
 
 #endif // DRC_DRILLED_HOLE_TESTER__H
diff --git a/pcbnew/drc/drc_keepout_tester.cpp b/pcbnew/drc/drc_keepout_tester.cpp
index 2a7851657b..ac549d5a65 100644
--- a/pcbnew/drc/drc_keepout_tester.cpp
+++ b/pcbnew/drc/drc_keepout_tester.cpp
@@ -51,7 +51,8 @@ bool DRC_KEEPOUT_TESTER::RunDRC( EDA_UNITS aUnits, BOARD& aBoard )
     // Test keepout areas for vias, tracks and pads inside keepout areas
     for( ZONE_CONTAINER* area : areasToInspect )
     {
-        m_keepoutFlags = area->GetKeepouts( &m_sources );
+        // JEY TODO: our existing keepout strategy needs a work-over for rules....
+        m_keepoutFlags = area->GetKeepouts( F_Cu, &m_sources );
 
         if( m_keepoutFlags > 0 )
         {
diff --git a/pcbnew/drc/drc_rule.cpp b/pcbnew/drc/drc_rule.cpp
index f9ec4614fb..b60898124e 100644
--- a/pcbnew/drc/drc_rule.cpp
+++ b/pcbnew/drc/drc_rule.cpp
@@ -23,13 +23,15 @@
 
 
 #include 
-#include 
 #include 
 #include 
+
+#include 
 #include 
 
 
-DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint )
+const DRC_CONSTRAINT* GetConstraint( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem,
+                                     int aConstraint, PCB_LAYER_ID aLayer, wxString* aRuleName )
 {
     BOARD* board = aItem->GetBoard();
 
@@ -38,13 +40,32 @@ DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstr
 
     for( DRC_RULE* rule : board->GetDesignSettings().m_DRCRules )
     {
-        if( ( rule->m_ConstraintFlags & aConstraint ) > 0 )
-        {
-            if( rule->m_Condition.EvaluateFor( aItem, bItem ) )
-                return rule;
+        if( !rule->m_LayerCondition.test( aLayer ) )
+            continue;
 
-            if( bItem && rule->m_Condition.EvaluateFor( bItem, aItem ) )
-                return rule;
+        for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
+        {
+            if( constraint.m_Type != aConstraint )
+                continue;
+
+            if( !rule->m_LayerCondition.test( aLayer ) )
+                continue;
+
+            if( rule->m_Condition.EvaluateFor( aItem, bItem, aLayer ) )
+            {
+                if( aRuleName )
+                    *aRuleName = rule->m_Name;
+
+                return &constraint;
+            }
+
+            if( bItem && rule->m_Condition.EvaluateFor( bItem, aItem, aLayer ) )
+            {
+                if( aRuleName )
+                    *aRuleName = rule->m_Name;
+
+                return &constraint;
+            }
         }
     }
 
@@ -52,6 +73,17 @@ DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstr
 }
 
 
+DRC_RULE::DRC_RULE() :
+    m_LayerCondition( LSET::AllLayersMask() )
+{
+}
+
+
+DRC_RULE::~DRC_RULE()
+{
+}
+
+
 DRC_RULE_CONDITION::DRC_RULE_CONDITION()
 {
     m_ucode = nullptr;
@@ -64,7 +96,8 @@ DRC_RULE_CONDITION::~DRC_RULE_CONDITION()
 }
 
 
-bool DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB )
+bool DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB,
+                                      PCB_LAYER_ID aLayer )
 {
     // An unconditional rule is always true
     if( m_Expression.IsEmpty() )
@@ -77,7 +110,7 @@ bool DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM
     BOARD_ITEM* a = const_cast( aItemA );
     BOARD_ITEM* b = aItemB ? const_cast( aItemB ) : DELETED_BOARD_ITEM::GetInstance();
 
-    PCB_EXPR_CONTEXT ctx;
+    PCB_EXPR_CONTEXT ctx( aLayer );
     ctx.SetItems( a, b );
 
     return m_ucode->Run( &ctx )->AsDouble() != 0.0;
@@ -91,14 +124,10 @@ bool DRC_RULE_CONDITION::Compile( REPORTER* aReporter, int aSourceLine, int aSou
     if (!m_ucode)
         m_ucode = new PCB_EXPR_UCODE;
 
-    PCB_EXPR_CONTEXT preflightContext;
+    PCB_EXPR_CONTEXT preflightContext( F_Cu );
 
     bool ok = compiler.Compile( m_Expression.ToUTF8().data(), m_ucode, &preflightContext );
-
-    if( ok )
-        return true;
-
-    return false;
+    return ok;
 }
 
 
diff --git a/pcbnew/drc/drc_rule.h b/pcbnew/drc/drc_rule.h
index 9f7e7da340..bc820ecd6f 100644
--- a/pcbnew/drc/drc_rule.h
+++ b/pcbnew/drc/drc_rule.h
@@ -25,8 +25,8 @@
 #define DRC_RULE_H
 
 #include 
-#include 
 #include 
+#include 
 #include 
 
 
@@ -34,12 +34,6 @@ class BOARD_ITEM;
 class PCB_EXPR_UCODE;
 
 
-#define CLEARANCE_CONSTRAINT (1 << 0)
-#define ANNULUS_CONSTRAINT   (1 << 1)
-#define TRACK_CONSTRAINT     (1 << 2)
-#define HOLE_CONSTRAINT      (1 << 3)
-#define DISALLOW_CONSTRAINT  (1 << 4)
-
 #define DISALLOW_VIAS        (1 << 0)
 #define DISALLOW_MICRO_VIAS  (1 << 1)
 #define DISALLOW_BB_VIAS     (1 << 2)
@@ -52,6 +46,17 @@ class PCB_EXPR_UCODE;
 #define DISALLOW_FOOTPRINTS  (1 << 9)
 
 
+enum DRC_RULE_ID_T
+{
+    DRC_RULE_ID_UNKNOWN = -1,
+    DRC_RULE_ID_CLEARANCE = 0,
+    DRC_RULE_ID_HOLE_SIZE,
+    DRC_RULE_ID_ANNULUS,
+    DRC_RULE_ID_TRACK,
+    DRC_RULE_ID_DISALLOW
+};
+
+
 template
 class MINOPTMAX
 {
@@ -78,19 +83,36 @@ private:
 };
 
 
+class DRC_CONSTRAINT
+{
+public:
+    DRC_CONSTRAINT() :
+        m_Type( DRC_RULE_ID_UNKNOWN ),
+        m_DisallowFlags( 0 ),
+        m_LayerCondition( LSET::AllLayersMask() )
+    {}
+
+    const MINOPTMAX& GetValue() const { return m_Value; }
+
+public:
+    DRC_RULE_ID_T  m_Type;
+    MINOPTMAX m_Value;
+    int            m_DisallowFlags;
+    LSET           m_LayerCondition;
+};
+
+
 class DRC_RULE_CONDITION
 {
 public:
     DRC_RULE_CONDITION();
     ~DRC_RULE_CONDITION();
 
-    bool EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB );
+    bool EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB, PCB_LAYER_ID aLayer );
     bool Compile( REPORTER* aReporter, int aSourceLine, int aSourceOffset );
 
 public:
-    LSET      m_LayerCondition;
     wxString  m_Expression;
-    wxString  m_TargetRuleName;
 
 private:
     PCB_EXPR_UCODE*       m_ucode;
@@ -100,40 +122,20 @@ private:
 class DRC_RULE
 {
 public:
-    DRC_RULE() :
-            m_ConstraintFlags( 0 ),
-            m_DisallowFlags( 0 ),
-            m_Clearance( { 0, 0, INT_MAX / 2 } ),
-            m_MinAnnulusWidth( 0 ),
-            m_TrackConstraint( { 0, 0, INT_MAX / 2 } ),
-            m_MinHole( 0 )
-    { }
-
-    struct MINOPTMAX
-    {
-        int Min;
-        int Opt;
-        int Max;
-    };
+    DRC_RULE();
+    virtual ~DRC_RULE();
 
 public:
-    wxString  m_Name;
-    int       m_ConstraintFlags;
-    int       m_DisallowFlags;
-
-    // A 0 value means the property is not modified by this rule.
-    // A positive value is a minimum.
-    MINOPTMAX m_Clearance;
-    int       m_MinAnnulusWidth;
-    MINOPTMAX m_TrackConstraint;
-    int       m_MinHole;
-
-    LSET               m_LayerCondition;
-    DRC_RULE_CONDITION m_Condition;
+    wxString                    m_Name;
+    LSET                        m_LayerCondition;
+    wxString                    m_TestProviderName;
+    DRC_RULE_CONDITION          m_Condition;
+    std::vector m_Constraints;
 };
 
 
-DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint );
+const DRC_CONSTRAINT* GetConstraint( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem,
+                                     int aConstraint, PCB_LAYER_ID aLayer, wxString* aRuleName );
 
 
 #endif      // DRC_RULE_H
diff --git a/pcbnew/drc/drc_rule_parser.cpp b/pcbnew/drc/drc_rule_parser.cpp
index 9c70f605f0..c5992e1baf 100644
--- a/pcbnew/drc/drc_rule_parser.cpp
+++ b/pcbnew/drc/drc_rule_parser.cpp
@@ -178,49 +178,6 @@ DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE()
 
         switch( token )
         {
-        case T_disallow:
-            token = NextTok();
-
-            if( (int) token == DSN_RIGHT )
-            {
-                reportError( _( "Missing disallowed type.|  Expected 'track', 'via', 'micro_via', "
-                                "'blind_via', 'pad', 'zone', 'text', 'graphic' or 'hole'." ) );
-                break;
-            }
-            else if( (int) token == DSN_STRING )
-            {
-                token = GetCurStrAsToken();
-            }
-
-            switch( token )
-            {
-            case T_track:      rule->m_DisallowFlags |= DISALLOW_TRACKS;     break;
-            case T_via:        rule->m_DisallowFlags |= DISALLOW_VIAS;       break;
-            case T_micro_via:  rule->m_DisallowFlags |= DISALLOW_MICRO_VIAS; break;
-            case T_buried_via: rule->m_DisallowFlags |= DISALLOW_BB_VIAS;    break;
-            case T_pad:        rule->m_DisallowFlags |= DISALLOW_PADS;       break;
-            case T_zone:       rule->m_DisallowFlags |= DISALLOW_ZONES;      break;
-            case T_text:       rule->m_DisallowFlags |= DISALLOW_TEXTS;      break;
-            case T_graphic:    rule->m_DisallowFlags |= DISALLOW_GRAPHICS;   break;
-            case T_hole:       rule->m_DisallowFlags |= DISALLOW_HOLES;      break;
-            case T_footprint:  rule->m_DisallowFlags |= DISALLOW_FOOTPRINTS; break;
-            default:
-                msg.Printf( _( "Unrecognized item '%s'.|  Expected 'track', 'via', 'micro_via', "
-                               "'blind_via', 'pad', 'zone', 'text', 'graphic' or 'hole'." ),
-                            FromUTF8() );
-                reportError( msg );
-            }
-
-            rule->m_ConstraintFlags = DISALLOW_CONSTRAINT;
-
-            if( (int) NextTok() != DSN_RIGHT )
-            {
-                reportError( wxString::Format( _( "Unrecognized item '%s'." ), FromUTF8() ) );
-                parseUnknown();
-            }
-
-            break;
-
         case T_constraint:
             parseConstraint( rule );
             break;
@@ -273,34 +230,65 @@ DRC_RULE* DRC_RULES_PARSER::parseDRC_RULE()
 
 void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
 {
-    T        token;
-    int      constraintType = 0;
-    int      value;
-    wxString msg;
+    aRule->m_Constraints.emplace_back( DRC_CONSTRAINT() );
 
-    token = NextTok();
+    DRC_CONSTRAINT& constraint = aRule->m_Constraints.back();
+    int             value;
+    wxString        msg;
+
+    T token = NextTok();
 
     if( (int) token == DSN_RIGHT )
     {
         reportError( _( "Missing constraint type.|  Expected 'clearance', 'track_width', "
-                        "'annulus_width' or 'hole'." ) );
+                        "'annulus_width', 'hole' or 'disallow'." ) );
         return;
     }
 
     switch( token )
     {
-    case T_clearance:     constraintType = CLEARANCE_CONSTRAINT; break;
-    case T_track_width:   constraintType = TRACK_CONSTRAINT;     break;
-    case T_annulus_width: constraintType = ANNULUS_CONSTRAINT;   break;
-    case T_hole:          constraintType = HOLE_CONSTRAINT;      break;
+    case T_clearance:     constraint.m_Type = DRC_RULE_ID_CLEARANCE; break;
+    case T_track_width:   constraint.m_Type = DRC_RULE_ID_TRACK;     break;
+    case T_annulus_width: constraint.m_Type = DRC_RULE_ID_ANNULUS;   break;
+    case T_hole:          constraint.m_Type = DRC_RULE_ID_HOLE_SIZE; break;
+    case T_disallow:      constraint.m_Type = DRC_RULE_ID_DISALLOW;  break;
     default:
         msg.Printf( _( "Unrecognized item '%s'.| Expected 'clearance', 'track_width', "
-                       "'annulus_width' or 'hole'." ),
+                       "'annulus_width', 'hole' or 'disallow'." ),
                     FromUTF8() );
         reportError( msg );
     }
 
-    aRule->m_ConstraintFlags |= constraintType;
+    if( constraint.m_Type == DRC_RULE_ID_DISALLOW )
+    {
+        for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
+        {
+            if( (int) token == DSN_STRING )
+                token = GetCurStrAsToken();
+
+            switch( token )
+            {
+            case T_track:      constraint.m_DisallowFlags |= DISALLOW_TRACKS;     break;
+            case T_via:        constraint.m_DisallowFlags |= DISALLOW_VIAS;       break;
+            case T_micro_via:  constraint.m_DisallowFlags |= DISALLOW_MICRO_VIAS; break;
+            case T_buried_via: constraint.m_DisallowFlags |= DISALLOW_BB_VIAS;    break;
+            case T_pad:        constraint.m_DisallowFlags |= DISALLOW_PADS;       break;
+            case T_zone:       constraint.m_DisallowFlags |= DISALLOW_ZONES;      break;
+            case T_text:       constraint.m_DisallowFlags |= DISALLOW_TEXTS;      break;
+            case T_graphic:    constraint.m_DisallowFlags |= DISALLOW_GRAPHICS;   break;
+            case T_hole:       constraint.m_DisallowFlags |= DISALLOW_HOLES;      break;
+            case T_footprint:  constraint.m_DisallowFlags |= DISALLOW_FOOTPRINTS; break;
+            default:
+                msg.Printf( _( "Unrecognized item '%s'.|  Expected 'track', 'via', 'micro_via', "
+                               "'blind_via', 'pad', 'zone', 'text', 'graphic' or 'hole'." ),
+                            FromUTF8() );
+                reportError( msg );
+                parseUnknown();
+            }
+        }
+
+        return;
+    }
 
     for( token = NextTok();  token != T_RIGHT;  token = NextTok() )
     {
@@ -321,14 +309,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
             }
 
             parseValueWithUnits( FromUTF8(), value );
-
-            switch( constraintType )
-            {
-            case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Min = value;       break;
-            case TRACK_CONSTRAINT:     aRule->m_TrackConstraint.Min = value; break;
-            case ANNULUS_CONSTRAINT:   aRule->m_MinAnnulusWidth = value;     break;
-            case HOLE_CONSTRAINT:      aRule->m_MinHole = value;             break;
-            }
+            constraint.m_Value.SetMin( value );
 
             if( (int) NextTok() != DSN_RIGHT )
             {
@@ -348,12 +329,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
             }
 
             parseValueWithUnits( FromUTF8(), value );
-
-            switch( constraintType )
-            {
-            case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Max = value;       break;
-            case TRACK_CONSTRAINT:     aRule->m_TrackConstraint.Max = value; break;
-            }
+            constraint.m_Value.SetMax( value );
 
             if( (int) NextTok() != DSN_RIGHT )
             {
@@ -373,12 +349,7 @@ void DRC_RULES_PARSER::parseConstraint( DRC_RULE* aRule )
             }
 
             parseValueWithUnits( FromUTF8(), value );
-
-            switch( constraintType )
-            {
-            case CLEARANCE_CONSTRAINT: aRule->m_Clearance.Opt = value;       break;
-            case TRACK_CONSTRAINT:     aRule->m_TrackConstraint.Opt = value; break;
-            }
+            constraint.m_Value.SetOpt( value );
 
             if( (int) NextTok() != DSN_RIGHT )
             {
diff --git a/pcbnew/exporters/gerber_jobfile_writer.cpp b/pcbnew/exporters/gerber_jobfile_writer.cpp
index 24f6daab41..424b040a8c 100644
--- a/pcbnew/exporters/gerber_jobfile_writer.cpp
+++ b/pcbnew/exporters/gerber_jobfile_writer.cpp
@@ -468,13 +468,17 @@ void GERBER_JOBFILE_WRITER::addJSONDesignRules()
 
     for( MODULE* module : m_pcb->Modules() )
     {
-        for( auto& pad : module->Pads() )
+        for( D_PAD* pad : module->Pads() )
         {
-            if( ( pad->GetLayerSet() & LSET::InternalCuMask() ).any() )
-                minPadClearanceInner = std::min( minPadClearanceInner, pad->GetClearance() );
+            for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
+            {
+                int padClearance = pad->GetClearance( layer );
 
-            if( ( pad->GetLayerSet() & LSET::ExternalCuMask() ).any() )
-                minPadClearanceOuter = std::min( minPadClearanceOuter, pad->GetClearance() );
+                if( layer == B_Cu || layer == F_Cu )
+                    minPadClearanceOuter = std::min( minPadClearanceOuter, padClearance );
+                else
+                    minPadClearanceInner = std::min( minPadClearanceInner, padClearance );
+            }
         }
     }
 
@@ -519,12 +523,15 @@ void GERBER_JOBFILE_WRITER::addJSONDesignRules()
         if( zone->GetIsKeepout() || !zone->IsOnCopperLayer() )
             continue;
 
-        int zclerance = zone->GetClearance();
+        for( PCB_LAYER_ID layer : zone->GetLayerSet().Seq() )
+        {
+            int zclerance = zone->GetClearance( layer );
 
-        if( zone->GetLayer() == B_Cu || zone->GetLayer() == F_Cu )
-            minclearanceOuter = std::min( minclearanceOuter, zclerance );
-        else
-            minclearanceInner = std::min( minclearanceInner, zclerance );
+            if( layer == B_Cu || layer == F_Cu )
+                minclearanceOuter = std::min( minclearanceOuter, zclerance );
+            else
+                minclearanceInner = std::min( minclearanceInner, zclerance );
+        }
     }
 
     if( minclearanceOuter != INT_MAX )
diff --git a/pcbnew/pcb_expr_evaluator.cpp b/pcbnew/pcb_expr_evaluator.cpp
index b19859efee..652245fd96 100644
--- a/pcbnew/pcb_expr_evaluator.cpp
+++ b/pcbnew/pcb_expr_evaluator.cpp
@@ -140,6 +140,63 @@ static void insideCourtyard( LIBEVAL::CONTEXT* aCtx, void* self )
 }
 
 
+static void insideArea( LIBEVAL::CONTEXT* aCtx, void* self )
+{
+    PCB_EXPR_CONTEXT* context = static_cast( aCtx );
+    LIBEVAL::VALUE*   arg = aCtx->Pop();
+    LIBEVAL::VALUE*   result = aCtx->AllocValue();
+
+    result->Set( 0.0 );
+    aCtx->Push( result );
+
+    if( !arg )
+    {
+        aCtx->ReportError( wxString::Format( _( "Missing argument to '%s'" ),
+                                             wxT( "insideArea()" ) ) );
+        return;
+    }
+
+    PCB_EXPR_VAR_REF* vref = static_cast( self );
+    BOARD_ITEM*       item = vref ? vref->GetObject( aCtx ) : nullptr;
+    ZONE_CONTAINER*   zone = nullptr;
+
+    if( !item )
+        return;
+
+    if( arg->AsString() == "A" )
+    {
+        zone = dynamic_cast( context->GetItem( 0 ) );
+    }
+    else if( arg->AsString() == "B" )
+    {
+        zone = dynamic_cast( context->GetItem( 1 ) );
+    }
+    else
+    {
+        for( ZONE_CONTAINER* candidate : item->GetBoard()->Zones() )
+        {
+            if( candidate->GetZoneName().Matches( arg->AsString() ) )
+            {
+                zone = candidate;
+                break;
+            }
+        }
+    }
+
+    if( zone )
+    {
+        SHAPE_POLY_SET zonePoly = zone->GetFilledPolysList( context->GetLayer() );
+        SHAPE_POLY_SET testPoly;
+
+        item->TransformShapeWithClearanceToPolygon( testPoly, 0 );
+        testPoly.BooleanIntersection( zonePoly, SHAPE_POLY_SET::PM_FAST );
+
+        if( testPoly.OutlineCount() )
+            result->Set( 1.0 );
+    }
+}
+
+
 PCB_EXPR_BUILTIN_FUNCTIONS::PCB_EXPR_BUILTIN_FUNCTIONS()
 {
     auto registerFunc = [&]( const wxString& funcSignature, FPTR funcPtr )
@@ -152,6 +209,7 @@ PCB_EXPR_BUILTIN_FUNCTIONS::PCB_EXPR_BUILTIN_FUNCTIONS()
     registerFunc( "onLayer('x')", onLayer );
     registerFunc( "isPlated()", isPlated );
     registerFunc( "insideCourtyard('x')", insideCourtyard );
+    registerFunc( "insideArea('x')", insideArea );
 }
 
 
diff --git a/pcbnew/pcb_expr_evaluator.h b/pcbnew/pcb_expr_evaluator.h
index df14336d05..7f760ca724 100644
--- a/pcbnew/pcb_expr_evaluator.h
+++ b/pcbnew/pcb_expr_evaluator.h
@@ -52,7 +52,8 @@ public:
 class PCB_EXPR_CONTEXT : public LIBEVAL::CONTEXT
 {
 public:
-    PCB_EXPR_CONTEXT()
+    PCB_EXPR_CONTEXT( PCB_LAYER_ID aLayer ) :
+            m_layer( aLayer )
     {
         m_items[0] = nullptr;
         m_items[1] = nullptr;
@@ -69,8 +70,14 @@ public:
         return m_items[index];
     }
 
+    PCB_LAYER_ID GetLayer() const
+    {
+        return m_layer;
+    }
+
 private:
-    BOARD_ITEM* m_items[2];
+    BOARD_ITEM*  m_items[2];
+    PCB_LAYER_ID m_layer;
 };
 
 
diff --git a/pcbnew/pcb_painter.cpp b/pcbnew/pcb_painter.cpp
index 9b3c754a61..67571de9e0 100644
--- a/pcbnew/pcb_painter.cpp
+++ b/pcbnew/pcb_painter.cpp
@@ -508,7 +508,8 @@ void PCB_PAINTER::draw( const TRACK* aTrack, int aLayer )
             m_gal->SetIsFill( false );
             m_gal->SetIsStroke( true );
             m_gal->SetStrokeColor( color );
-            m_gal->DrawSegment( start, end, width + aTrack->GetClearance() * 2 );
+            m_gal->DrawSegment( start, end,
+                                width + aTrack->GetClearance( ToLAYER_ID( aLayer ) ) * 2 );
         }
     }
 }
@@ -547,7 +548,7 @@ void PCB_PAINTER::draw( const ARC* aArc, int aLayer )
             m_gal->SetStrokeColor( color );
 
             m_gal->DrawArcSegment( center, radius, start_angle, start_angle + angle,
-                    width + aArc->GetClearance() * 2 );
+                                   width + aArc->GetClearance( ToLAYER_ID( aLayer ) ) * 2 );
         }
     }
 }
@@ -688,7 +689,7 @@ void PCB_PAINTER::draw( const VIA* aVia, int aLayer )
         m_gal->SetIsFill( false );
         m_gal->SetIsStroke( true );
         m_gal->SetStrokeColor( color );
-        m_gal->DrawCircle( center, radius + aVia->GetClearance() );
+        m_gal->DrawCircle( center, radius + aVia->GetClearance( aVia->GetLayer() ) );
     }
 }
 
@@ -894,7 +895,7 @@ void PCB_PAINTER::draw( const D_PAD* aPad, int aLayer )
         m_gal->SetIsStroke( true );
         m_gal->SetIsFill( false );
         m_gal->SetStrokeColor( color );
-        int clearance = aPad->GetClearance();
+        int clearance = aPad->GetClearance( aPad->GetLayer() );
 
         const std::shared_ptr shapes =
                     std::dynamic_pointer_cast( aPad->GetEffectiveShape() );
diff --git a/pcbnew/tools/drawing_tool.cpp b/pcbnew/tools/drawing_tool.cpp
index cdf9099f71..3a5717b6c8 100644
--- a/pcbnew/tools/drawing_tool.cpp
+++ b/pcbnew/tools/drawing_tool.cpp
@@ -1750,9 +1750,10 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
                 if( !(item->GetLayerSet() & lset ).any() )
                     continue;
 
-                if( auto track = dyn_cast( item ) )
+                if( TRACK* track = dyn_cast( item ) )
                 {
-                    int max_clearance = std::max( clearance, track->GetClearance() );
+                    int max_clearance = std::max( clearance,
+                                                  track->GetClearance( track->GetLayer() ) );
 
                     if( TestSegmentHit( position, track->GetStart(), track->GetEnd(),
                             ( track->GetWidth() + aVia->GetWidth() ) / 2  + max_clearance ) )
@@ -1761,11 +1762,11 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
                             return true;
 
                         net = track->GetNetCode();
-                        clearance = track->GetClearance();
+                        clearance = track->GetClearance( track->GetLayer() );
                     }
                 }
 
-                if( auto via = dyn_cast( item ) )
+                if( VIA* via = dyn_cast( item ) )
                 {
                     int dist = KiROUND( GetLineLength( position, via->GetPosition() ) );
 
@@ -1773,27 +1774,30 @@ int DRAWING_TOOL::DrawVia( const TOOL_EVENT& aEvent )
                         return true;
                 }
 
-                if( auto mod = dyn_cast( item ) )
+                if( MODULE* mod = dyn_cast( item ) )
                 {
                     for( D_PAD* pad : mod->Pads() )
                     {
-                        int max_clearance = std::max( clearance, pad->GetClearance() );
-
-                        if( pad->HitTest( aVia->GetBoundingBox(), false, max_clearance ) )
+                        for( PCB_LAYER_ID layer : pad->GetLayerSet().Seq() )
                         {
-                            if( net && pad->GetNetCode() != net )
-                                return true;
+                            int max_clearance = std::max( clearance, pad->GetClearance( layer ) );
 
-                            net = pad->GetNetCode();
-                            clearance = pad->GetClearance();
-                        }
+                            if( pad->HitTest( aVia->GetBoundingBox(), false, max_clearance ) )
+                            {
+                                if( net && pad->GetNetCode() != net )
+                                    return true;
 
-                        if( pad->GetDrillSize().x && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
-                        {
-                            int dist = KiROUND( GetLineLength( position, pad->GetPosition() ) );
+                                net = pad->GetNetCode();
+                                clearance = pad->GetClearance( layer );
+                            }
 
-                            if( dist < drillRadius + pad->GetDrillSize().x / 2 + holeToHoleMin )
-                                return true;
+                            if( pad->GetDrillSize().x && pad->GetDrillShape() == PAD_DRILL_SHAPE_CIRCLE )
+                            {
+                                int dist = KiROUND( GetLineLength( position, pad->GetPosition() ) );
+
+                                if( dist < drillRadius + pad->GetDrillSize().x / 2 + holeToHoleMin )
+                                    return true;
+                            }
                         }
                     }
                 }
diff --git a/pcbnew/zone_filler.cpp b/pcbnew/zone_filler.cpp
index 67521ea744..195a8c9d87 100644
--- a/pcbnew/zone_filler.cpp
+++ b/pcbnew/zone_filler.cpp
@@ -656,7 +656,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
                     if( pad->GetNetCode() > 0 && pad->GetNetCode() == aZone->GetNetCode() )
                         gap = std::max( zone_clearance, aZone->GetThermalReliefGap( pad ) );
                     else
-                        gap = aZone->GetClearance( pad );
+                        gap = aZone->GetClearance( aLayer, pad );
 
                     addKnockout( pad, gap, aHoles );
                 }
@@ -676,7 +676,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
 
         if( track->GetBoundingBox().Intersects( zone_boundingbox ) )
         {
-            int gap = aZone->GetClearance( track ) + extra_margin;
+            int gap = aZone->GetClearance( aLayer, track ) + extra_margin;
 
             track->TransformShapeWithClearanceToPolygon( aHoles, gap, m_low_def );
         }
@@ -695,7 +695,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
                 if( aItem->GetBoundingBox().Intersects( zone_boundingbox ) )
                 {
                     bool ignoreLineWidth = aItem->IsOnLayer( Edge_Cuts );
-                    int  gap = aZone->GetClearance( aItem );
+                    int  gap = aZone->GetClearance( aLayer, aItem );
 
                     addKnockout( aItem, gap, ignoreLineWidth, aHoles );
                 }
@@ -739,7 +739,7 @@ void ZONE_FILLER::buildCopperItemClearances( const ZONE_CONTAINER* aZone, PCB_LA
             int  gap = 0;
 
             if( !zone->GetIsKeepout() && aZone->GetNetCode() != zone->GetNetCode() )
-                gap = aZone->GetClearance( zone );
+                gap = aZone->GetClearance( aLayer, zone );
 
             zone->TransformOutlinesShapeWithClearanceToPolygon( aHoles, gap );
         }
diff --git a/qa/drc_proto/drc.cpp b/qa/drc_proto/drc.cpp
index 0a8de43e97..77645fed61 100644
--- a/qa/drc_proto/drc.cpp
+++ b/qa/drc_proto/drc.cpp
@@ -536,8 +536,7 @@ void DRC::testPadClearances( BOARD_COMMIT& aCommit )
             static DRAWSEGMENT dummyEdge;
             dummyEdge.SetLayer( Edge_Cuts );
 
-            if( pad->GetRuleClearance( &dummyEdge, &minClearance, &m_clearanceSource ) )
-                /* minClearance and m_clearanceSource set in GetRuleClearance() */;
+            pad->GetRuleClearance( &dummyEdge, pad->GetLayer(), &minClearance, &m_clearanceSource );
 
             for( auto it = m_board_outlines.IterateSegmentsWithHoles(); it; it++ )
             {
diff --git a/qa/drc_proto/drc_clearance_test_functions.cpp b/qa/drc_proto/drc_clearance_test_functions.cpp
index 736d9a3e39..ba67583a6d 100644
--- a/qa/drc_proto/drc_clearance_test_functions.cpp
+++ b/qa/drc_proto/drc_clearance_test_functions.cpp
@@ -391,7 +391,7 @@ void DRC::doTrackDrc( BOARD_COMMIT& aCommit, TRACK* aRefSeg, TRACKS::iterator aS
                 dummypad.SetLayerSet( all_cu | dummypad.GetLayerSet() );
 
                 int       minClearance;
-                DRC_RULE* rule = GetRule( aRefSeg, &dummypad, CLEARANCE_CONSTRAINT );
+                DRC_RULE* rule = GetRule( aRefSeg, &dummypad, DRC_RULE_ID_CLEARANCE );
 
                 if( rule )
                 {
diff --git a/qa/drc_proto/drc_drilled_hole_tester.cpp b/qa/drc_proto/drc_drilled_hole_tester.cpp
index eeca399e5e..c68f082434 100644
--- a/qa/drc_proto/drc_drilled_hole_tester.cpp
+++ b/qa/drc_proto/drc_drilled_hole_tester.cpp
@@ -93,7 +93,7 @@ bool DRC_DRILLED_HOLE_TESTER::checkPad( D_PAD* aPad )
     {
         int       minHole = bds.m_MinThroughDrill;
         wxString  minHoleSource = _( "board minimum" );
-        DRC_RULE* rule = GetRule( aPad, nullptr, HOLE_CONSTRAINT );
+        DRC_RULE* rule = GetRule( aPad, nullptr, DRC_RULE_ID_HOLE_SIZE );
 
         if( rule )
         {
@@ -136,7 +136,7 @@ bool DRC_DRILLED_HOLE_TESTER::checkVia( VIA* via )
     {
         int       minHole = bds.m_MinThroughDrill;
         wxString  minHoleSource = _( "board minimum" );
-        DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT );
+        DRC_RULE* rule = GetRule( via, nullptr, DRC_RULE_ID_HOLE_SIZE );
 
         if( rule )
         {
@@ -179,7 +179,7 @@ bool DRC_DRILLED_HOLE_TESTER::checkMicroVia( VIA* via )
     {
         int       minHole = bds.m_MicroViasMinDrill;
         wxString  minHoleSource = _( "board minimum" );
-        DRC_RULE* rule = GetRule( via, nullptr, HOLE_CONSTRAINT );
+        DRC_RULE* rule = GetRule( via, nullptr, DRC_RULE_ID_HOLE_SIZE );
 
         if( rule )
         {
diff --git a/qa/drc_proto/drc_engine.cpp b/qa/drc_proto/drc_engine.cpp
index 51a83b3a0f..f1c61bc28c 100644
--- a/qa/drc_proto/drc_engine.cpp
+++ b/qa/drc_proto/drc_engine.cpp
@@ -245,7 +245,7 @@ test::DRC_RULE* test::DRC_ENGINE::EvalRulesForItems( test::DRC_RULE_ID_T ruleID,
             drc_dbg( 8, "   -> check condition '%s'\n",
                     (const char*) condition->m_Expression.c_str() );
 
-            bool result = condition->EvaluateFor( a, b );
+            bool result = condition->EvaluateFor( a, b, F_Cu ); // FIXME: need the actual layer
             if( result )
             {
                 drc_dbg( 8, "   -> rule '%s' matches, triggered by condition '%s'\n",
diff --git a/qa/drc_proto/drc_rule.cpp b/qa/drc_proto/drc_rule.cpp
index 5055cad77d..37e17e44da 100644
--- a/qa/drc_proto/drc_rule.cpp
+++ b/qa/drc_proto/drc_rule.cpp
@@ -26,7 +26,6 @@
 #include 
 #include 
 
-
 #include 
 #include 
 
@@ -37,14 +36,14 @@ test::DRC_RULE::DRC_RULE() :
     m_Conditional( false ),
     m_Priority( 0 )
 {
-
 }
 
+
 test::DRC_RULE::~DRC_RULE()
 {
-
 }
 
+
 test::DRC_RULE_CONDITION::DRC_RULE_CONDITION()
 {
     m_ucode = nullptr;
@@ -57,11 +56,20 @@ test::DRC_RULE_CONDITION::~DRC_RULE_CONDITION()
 }
 
 
-bool test::DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB )
+bool test::DRC_RULE_CONDITION::EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB,
+                                            PCB_LAYER_ID aLayer )
 {
+    // An unconditional rule is always true
+    if( m_Expression.IsEmpty() )
+        return true;
+
+    // A rule which failed to compile is always false
+    if( !m_ucode )
+        return false;
+
     BOARD_ITEM* a = const_cast( aItemA );
     BOARD_ITEM* b = aItemB ? const_cast( aItemB ) : DELETED_BOARD_ITEM::GetInstance();
-    PCB_EXPR_CONTEXT ctx;
+    PCB_EXPR_CONTEXT ctx( aLayer );
     ctx.SetItems( a, b );
 
     return m_ucode->Run( &ctx )->AsDouble() != 0.0;
@@ -75,7 +83,7 @@ bool test::DRC_RULE_CONDITION::Compile( REPORTER* aReporter, int aSourceLine, in
     if (!m_ucode)
         m_ucode = new PCB_EXPR_UCODE;
 
-    LIBEVAL::CONTEXT preflightContext;
+    PCB_EXPR_CONTEXT preflightContext( F_Cu );
 
     bool ok = compiler.Compile( m_Expression.ToUTF8().data(), m_ucode, &preflightContext );
     return ok;
diff --git a/qa/drc_proto/drc_rule.h b/qa/drc_proto/drc_rule.h
index 6df2e3097f..3b0516e67c 100644
--- a/qa/drc_proto/drc_rule.h
+++ b/qa/drc_proto/drc_rule.h
@@ -34,7 +34,6 @@ class BOARD_ITEM;
 namespace LIBEVAL
 {
 class UCODE;
-class ERROR_STATUS;
 };
 
 class PCB_EXPR_UCODE;
@@ -42,14 +41,20 @@ class PCB_EXPR_UCODE;
 namespace test
 {
 
-enum class DRC_RULE_ID_T {
+enum class DRC_RULE_ID_T
+{
+    DRC_RULE_ID_UNKNOWN = -1,
     DRC_RULE_ID_CLEARANCE = 0,
     DRC_RULE_ID_HOLE_CLEARANCE,
     DRC_RULE_ID_EDGE_CLEARANCE,
-    DRC_RULE_ID_HOLE_SIZE
+    DRC_RULE_ID_HOLE_SIZE,
+    DRC_RULE_ID_ANNULUS,
+    DRC_RULE_ID_TRACK,
+    DRC_RULE_ID_DISALLOW
 };
 
-enum class DRC_RULE_SEVERITY_T {
+enum class DRC_RULE_SEVERITY_T
+{
     DRC_SEVERITY_IGNORE = 0,
     DRC_SEVERITY_WARNING,
     DRC_SEVERITY_ERROR
@@ -80,17 +85,48 @@ private:
     bool m_hasMax = false;
 };
 
+
 class DRC_CONSTRAINT
 {
-    public:
+public:
+    DRC_CONSTRAINT() :
+        m_Type( DRC_RULE_ID_T::DRC_RULE_ID_UNKNOWN ),
+        m_DisallowFlags( 0 ),
+        m_LayerCondition( LSET::AllLayersMask() )
+    {
+    }
+
     const MINOPTMAX& GetValue() const { return m_Value; }
     bool Allowed() const { return m_Allow; }
 
-    public:
-        MINOPTMAX m_Value;
-        bool      m_Allow;
+public:
+    DRC_RULE_ID_T  m_Type;
+    MINOPTMAX m_Value;
+    int            m_DisallowFlags;
+    LSET           m_LayerCondition;
+
+    bool      m_Allow;
 };
 
+
+class DRC_RULE_CONDITION
+   {
+   public:
+       DRC_RULE_CONDITION();
+       ~DRC_RULE_CONDITION();
+
+       bool EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB, PCB_LAYER_ID aLayer );
+       bool Compile( REPORTER* aReporter, int aSourceLine = 0, int aSourceOffset = 0 );
+
+   public:
+       wxString  m_Expression;
+       wxString  m_TargetRuleName;
+
+   private:
+       PCB_EXPR_UCODE*       m_ucode;
+   };
+
+
 class DRC_RULE
 {
 public:
@@ -115,37 +151,23 @@ public:
 public:
     bool m_Unary;
 
-    wxString m_Name;
-    wxString m_TestProviderName;
+    wxString                    m_Name;
+    LSET                        m_LayerCondition;
+    wxString                    m_TestProviderName;
+    DRC_RULE_CONDITION          m_Condition;
+    std::vector m_Constraints;
+
     DRC_RULE_SEVERITY_T m_Severity;
     bool m_Enabled;
     bool m_Conditional;
     int  m_Priority; // 0 indicates automatic priority generation
 
-    DRC_CONSTRAINT m_Constraint;
+    DRC_CONSTRAINT m_Constraint;    // FIXME: move to m_Constraints
 };
 
 
-class DRC_RULE_CONDITION
-{
-public:
-    DRC_RULE_CONDITION();
-    ~DRC_RULE_CONDITION();
-
-    bool EvaluateFor( const BOARD_ITEM* aItemA, const BOARD_ITEM* aItemB );
-    bool Compile( REPORTER* aReporter, int aSourceLine = 0, int aSourceOffset = 0 );
-
-public:
-    LSET      m_LayerCondition;
-    wxString  m_Expression;
-    wxString  m_TargetRuleName;
-
-private:
-    PCB_EXPR_UCODE*       m_ucode;
-};
-
-
-//DRC_RULE* GetRule( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem, int aConstraint );
+//const DRC_CONSTRAINT* GetConstraint( const BOARD_ITEM* aItem, const BOARD_ITEM* bItem,
+//                                     int aConstraint, PCB_LAYER_ID aLayer, wxString* aRuleName );
 
 }; // namespace test